Test Driven Development with . NET Core | Trevoir Williams | Skillshare
Search

Vitesse de lecture


1.0x


  • 0.5x
  • 0.75x
  • 1 x (normale)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Développement piloté par des tests avec . NET Core

teacher avatar Trevoir Williams, Jamaican Software Engineer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Introduction

      5:14

    • 2.

      Créer un projet de test

      7:25

    • 3.

      Test unitaire : créer un test unitaire en fonction des besoins

      15:40

    • 4.

      Mettre en œuvre : écrire du code pour retourner des données correctes

      3:31

    • 5.

      Refactor : code de test et de mise en œuvre

      7:04

    • 6.

      Passez en revue la liste complète des exigences avec les projets GitHub

      5:56

    • 7.

      Tester et mettre en œuvre : lancer l'exception lorsque la demande est nulle

      7:52

    • 8.

      Créer des services de réservation

      4:05

    • 9.

      Test à l'unité : enregistrer une réservation de chambre

      12:50

    • 10.

      Mettre en œuvre : enregistrer une réservation de chambre

      8:52

    • 11.

      Test à l'unité : vérifier si de la place est disponible

      6:34

    • 12.

      Mettre en œuvre : vérifier si de la place est disponible

      6:25

    • 13.

      Tester et mettre en œuvre : ajouter l'identifiant de pièce au résultat

      5:22

    • 14.

      Tester et mettre en œuvre : retourner un résultat de réservation réussi ou échoué

      9:32

    • 15.

      Tester et mettre en œuvre : ajouter l'identifiant de réservation au résultat

      6:27

    • 16.

      Revue de section

      1:35

    • 17.

      Projet de refactorer pour la couche d'accès aux données

      11:02

    • 18.

      Test à l'unité : récupérer les pièces disponibles

      13:50

    • 19.

      Mettre en œuvre : récupérer les pièces disponibles

      9:31

    • 20.

      Tester et mettre en œuvre : enregistrer une réservation de chambre

      7:45

    • 21.

      Revue de section

      3:18

    • 22.

      Comprendre les tests unitaires . Applications NET Core

      10:27

    • 23.

      Stockage de données en mémoire Sqlite

      4:08

    • 24.

      Créer des scénarios de test unitaire

      11:32

    • 25.

      Mettre en œuvre le code pour les tests

      16:32

    • 26.

      Exécuter et tester l'application ASP.NET Core

      9:14

    • 27.

      Examen de cours

      4:06

  • --
  • Niveau débutant
  • Niveau intermédiaire
  • Niveau avancé
  • Tous niveaux

Généré par la communauté

Le niveau est déterminé par l'opinion majoritaire des apprenants qui ont évalué ce cours. La recommandation de l'enseignant est affichée jusqu'à ce qu'au moins 5 réponses d'apprenants soient collectées.

73

apprenants

--

projet

About This Class

Le développement piloté par les tests (TDD) est une approche puissante pour créer des logiciels solides et robustes. Dans ce cours, vous apprendrez les compétences dont vous avez besoin pour être en mesure d'appliquer TDD dans votre système. Projets NET. Le test unitaire est un type de test logiciel où le code est écrit pour automatiser le test des sous-sections d'une application entière. Cette méthodologie permet d'obtenir une couverture plus fonctionnelle et réduit le besoin de tests de régression manuels lorsque le code de l'application change. Le but est de valider que chaque unité du code logiciel fonctionne comme prévu.

Vous découvrirez les bases des tests unitaires et sur le cycle de refactor rouge-vert. Vous apprendrez également à tester et à mettre en œuvre la logique d'entreprise dans un environnement . Application NET Core utilisant xUnit comme cadre de test et Moq comme bibliothèque moqueur.

Après ce cours, vous aurez les bases du développement piloté par les tests qui vous aideront à tester et à implémenter de nouvelles fonctionnalités dans vos projets C #. Vous aurez les compétences et l'expérience nécessaires pour créer une application ASP.NET Core testable et maintenable pour architecturer les entreprises dans le monde réel . Applications NET Core.

Créer des bases solides dans les tests unitaires dans . NET :

  • Utiliser les projets de test xUnit

  • Utiliser Moq et Shouldly pour écrire des tests unitaires

  • Projets GitHub pour suivre les exigences
  • Développement basé sur les tests de refactoring en rouge et vert (TDD)

  • Pratiquer le développement piloté par des tests dans du code réel

  • Apprendre à écrire du code testable

  • Comprendre comment écrire des tests unitaires, puis créer ou refactorer du code

  • Examiner les pièges à éviter et les défis communs

Le cours est complet avec les fichiers de travail hébergés sur GitHub, avec l'inclusion de certains fichiers pour vous faciliter la réplication du code en cours de démonstration. 

Rencontrez votre enseignant·e

Teacher Profile Image

Trevoir Williams

Jamaican Software Engineer

Enseignant·e

Compétences associées

Développement Développement Web
Level: All Levels

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction: Welcome to test-driven development in dotnet Core, the handbook. I'm your instructor travel or Williams and I'm a software engineer and literature for over a decade. Now you're probably curious you saw the course title and you're wondering what exactly is test-driven development? Well, it test-driven development is a software development process that relies on requirements being written as test cases before real code is written. So that's right, You read that right? We do the unit tests. First. I know that the word test is what drove you to this course. And you're looking at the word test, I'm wondering, okay, I know a little bit about what unit tests are, but I'm not necessarily sure as to how I can use them in my code. Well, this is the perfect course for you. So TDD is a process of ensuring that we write testable code and it is modular enough and it is simply enough to get the task done. It is also a process that ensures that we have maximum code coverage. So when we make breaking changes and later on in the code, there won't necessarily be breaking, but they will fit right in. Now we have what we call the test-driven development cycle. In this cycle, we do our red test, meaning we write the unit test. There's no code, no supporting code, but as we model on the test, we have to create all the artifacts that would be needed in the real code anyway. So this helps us with our initial API or initial code design concepts. Then we write the code too much the tests that we have written. So we wrote a test to match the requirement nowhere writing the code to match the test. And then the ultimate outcome of this would be that we write the code to match the test and we test again and get a green test. So the red test means it fails. There's no code to support this test. We write the code and then we test again, and this test should now pass. So that means the code that we have written is now going to fulfill the requirement for which the test was written. So as we do this and or requirements grow or our understanding of what needs to be implemented improves. We refactor and repeat this process. Now, many developers Nobel unit testing, but not many developers actually practice it. Religious, they are practiced it to the extent that they need to. 1, it tells you to write testable code. You're always going to be thinkable writing your code in a way that I can test different aspects of it without having to worry about initializing or mocking too many things at once. But all of the things are injectable. So it helps to enforce what we call the solid principles from the get-go. It usually leads to much cleaner and succinct code and usually involves you putting in proper practices when writing your code. Also, you get almost instant feedback as to whether or not your code works because you're just going to do an automated tests against a test case. So you create a scenario where your test should pass, probably one where if the data was, but it should do this. You just create your scenarios and you get instant feedback. Citizens have to wait how long for an application to spool up. And then you put in the values and then you see the feedback. You can automate these things and it will greatly reduce the amount of time you spend testing and retesting code. Now with all that said, there are disadvantages and some of them include the fact that it's time-consuming because you're actually writing twice as much code as you probably would have if you were just writing the code from the get-go. It might also be difficult for beginners to get the initially, but hey, that's why we're all here right now this course is very beginner friendly. You learn how to use ECC unit to make tests projects hold to use third-party libraries to make testing and assertions much easier. We will look at how we do the whole test cycle. So we'll write tests then implement the code, and then we are factor as application grows and we look at just test-driven development and throw this course, we will review some of the pitfalls, but ultimately we want to just increase your unit testing skills and help you to have an appreciation of why it's important and how it can be done with maximum efficiency. Now a quick preview of what we will be doing. One, we're going to be building a real application. It's not going to be the most complicated application, but it's going to be just enough that we can see how different levels of unit testing can work, how it evolves over time, and some of the basic or high level requirements artifact, that one, It's a room booking application. So I have to put in logic to govern. Whole room is booked, what's requested, what kind of feedback the application is going to give. And we are eventually going to hooked up to a database. So we need to make sure all of those connections work. So I'm telling you that at the end of this course, we would have built something close to what our real enterprise application would look like. And we would have done it using test-driven development. So with all that said and done, this is test-driven development with dotnet Core, the handbook. And I can't wait to see you in the course. 2. Create Test Project: All right guys, So let's continue in this course and we're going to start off by setting up artists projects. So if you don't already have Visual Studio, he can go ahead and get 2019 are the latest version available to you at the time of this recording. But what we're going to do is go ahead and create a new project. And we want an x unit tests projects or just it's a search for x units. Now, just note that yes, we're using x unit for this course. But the general principles of unit testing can be applied to an end unit project or even a regular Microsoft test project. Because if you just type in test or a test project, I'm sure you'll see MSTest. There we go. And unit test project as well as the X unit tests projects. So I'll be showing you using x units, but the general principles of unit testing will remain true. So let's go ahead and hit H units. And we will be calling this project room booking up dot core, dot. So room bulking up name of the application. Core is going to be where we have all the business logic. So I'm just calling it dot core, but then we'll be writing unit tests around the core project. For the solution name. We're going to rename that. So I'm just going to say room booking up, that's into solution, but right now we're just dealing with a test project. So again, go ahead and create those, hit Next. And yes, we're using dotnet five time of this recording. If there's a little version and feel free to use it, which would probably be done at six. And then we go ahead and hit Create. So the end result of that activity is our getting this new project room booking dot core dot tests. We also get that default file called unittest one. Now I'm all into writing a unit test in this particular video, but we're going to walk through certain configurations that the thing will just make it easier for us moving forward. I will also look at the basics of what a unit test is. So the first thing that I'm going to do is right-click on our project file for the test project and go to Properties. And what I'm going to do is change the default namespace. Sometimes my Visual Studio glitchy surface of the switch 15 tabs. But we're going to change the default namespace to Booking dot core, someone to remove that because what happens is that it actually makes it easier to share resources across the projects, the test project, and the actual dot core project when he comes online. And if they have the same namespace, then it's easy to share the resources that definitely will need to be shared between the two. So I'm just making sure that everything is okay with that and then I can save and close that. Now, I did say configurations, but that's really the only one that we need at least, right? No. So in terms of the basics of a unit test, you notice that one, we have this annotation above the method called fat. Now this is what tells the IDE, the program everything that this is just a unit test on, factor this into the entire run-time of the application. It's a unit tests. So anytime we're writing unit tests, we will want to at least have this annotation that says fat. And other annotation that we're going to be looking at is theory. But we will get there eventually. So that is step number one. The next thing to note is that a unit test usually has three steps to it. You have the orange, we have the heart, and then we have assert. So generally speaking, the Iranges where you prep all the data, you prepare your test artifacts. You know, whenever you are going to be testing something using a user interface, usually know a value you need to put in to get what kind of response for whatever outcome you're testing for. So that's is what they're arranged portion is forward when to create objects are representations of whatever data we would expect to be testing against. So that's what we do, we create them. Then we're going to optimize anywhere when to actually simulate a call to the method that we hope should be called when we enact such an action, then our assertions null would be what see whether the test will pass or fail. Because then we'll have to assert that this is the old column with this input to this method. Or did it happen? Did it not happen? So if we said this should happen and it did not happen, that test will basically feel. And then that's when we'll have to either write the code to make it green or it would feel are the role of the court and ran the test. That means there's probably something wrong with the code or something wrong along the way. So that is basically how a unit test works. That's just the theory. And I'm not I'm trying to keep it as simple as possible so that you don't think it's too hard or too difficult to conceptualize how this thing will work. Well, Visual Studio makes it very easy to navigate through tests because they give us a Test Explorer. And you can get to this text Test Explorer by going to View and then clicking Test Explorer or doing Control E and T. So this Test Explorer is basically just going to be an interface that shows us all of the unit tests that are pending, all of the tests, projects one, all of the unit tests instead projects AC we have unit test 1, that's our class I in there we have test1, right? And then it would allow us to run these tests. Now I just clicked Run, but nothing is happening because there's nothing to run. So essentially whenever we write unit tests are owned a particular method, you would actually see the navigation properties coming up inside of Visual Studio to show which method has how many tests, how many tests are failing or passing ideas, this one method, et cetera. Another grid benefits of unit tests in your quote is that you can set up what we call gated builds in certain DevOps pipelines. So DevOps pipelines when you wanted to continuous development, continuous integration, you're working on a team, different people cheated in different parts of the code. You want to ensure that when somebody checks in their code that all the unit tests that use the work still work. And any new ones, they introduced our policy, sorry that they pass not work. How biologists say I wanted to make sure that all tests are passing before you allow that new code into a central repository for the entire team. So unit testing can act as a guard against that. So with all that said and done, once again, just a little theory, just a little understanding as to why we would be writing unit tests. And when we come back, we're going to read our first test for our first requirement. 3. Unit Test: Create Unit Test Based On Requirement: All right guys, so let us go ahead and create our very first unit test. Our first requirement is that when a particular method is called to process the booking, we expect a certain kind of results with all of the booking details included. So that is our first requirement that we're going to meet. Now, of course, as we get a better understanding of other requirements are quote, unquote blocks will evolve. So I'm just doing it in a minute. Blocks so that we can see how the code evolves, how we refactor hall, we retest and all we repeat, and basically how the cycle works. So with all that said and done enough of my monologue, let's get started. So I'm going to delete the unit test that we are currently seeing and we're going to create a new class. And I went to call this one room booking request processor test for descriptive, or there's nothing wrong with being descriptive with their class names. Anybody it looks at your class should know what's going on. So this is a test for our room booking requests, the processor that we intend to build eventually. So I'm going to go ahead and add that, meet the class public. And then inside of this, I'm going to have public void. Should return room booking a request. And it's a method, of course, no, I wanted to pause here and 0 that there are different conventions when naming to some people, they use the underscores. Some DB2 don't use underscores. They prefer to have it all camelCase or Pascal case rather. So there are different conventions. Some Ebola, right? One to pass, one to fail, and you have different ways of approaching it. So I'm just pointing out, I'm going to try and use the underscores for the thing it does. I liked the underscores in test set or really that using them anywhere else. I like to use them when naming my test methods. You might not, but the point is that you do whatever is comfortable with you as long as your code is readable. Alright? So inside of this test, and once again it's a test so we have to make sure that we give it our attributes, which is Fox control dot, to include the missing namespace for x unit. There we go. So that might be something else, that annotation might be something else if you're using a different testing project. But once again, I'm focusing on x units in this course. Now what we're going to do is the orange Shawn off this whole test threats, which is to create our test artifacts. So I need an instance of the actual processor that we need to test. So I'll create var room booking processor, or I would just call it processor, is equal to a new instance of something that doesn't yet exist. Okay? So how do we make it exist? Well, I'm just going to do Control dots. I don't want to say it generates the class in a new file, so that's it. Go ahead and create that so it's null in existence. You can go ahead and fill it with all the properties we know that it's going to need are the methods that it's going to need. Well, before I do that, what I'm going to do is RE in some sin and diarrhea and Jean phase. So why lm thinkable the requirements? I know that's one request needs to come over and two, I need to return something with the same values from the request that just came in. This is the processor that's going to handle the request and do all the business logic. But ultimately, I know that in my arrange I need to Mach what our requests will look like. So I'm just going to say var booking request is equal to a new instance off and I'm just going to use room booking request. And this is going to have a few properties. So never mind the fact that these things don't exist. That's a point of test-driven development. Modelling what? So I know the application is going to need to complete certain tasks, right? So booking request is equal to new room booking requests. And this is going to have or the fields full name. And I can just put in test name. So once again, these are testfile is they don't necessarily have to be realized what they need to be really enough to represent what might come in. So test request.com. And then we have the date, which I can say is equal to put any new date, time where we pass in the year, the month, and the day. So that is what this will look like. This object is booking request object. So after doing all of that, I can know say control dot legit, no, it should create a new file and look our Visual Studio so intelligently doing it is saying generate the class with these properties and I'll say why. Thank you. I would very much like you to do that. And I can just jump over and make sure everything is okay with that. And that looks fine to me. So I'll just go back to my test and continents. So I have the processor class created, I have the request cross created. No, I need a method that would actually process the request. We're, but let's put that done inside of the processors. So I'm going to put that processor is going to have a method, of course non exists. But it's going to be called the book room, which will take in the request. Alright, so I'm calling my methods is R. This is what I plan to call the methods. Of course I could have called this process requests or something else, but I'm just plotting it will groom. It takes in our request object. And we are suppose to return, right? We should return. So this has returned room requests, room booking requests. So I'm going to say or should return room booking response. And I'll just say which values are with requests values. I think that's clear enough, Fred. So anybody who reads that will know that this is what the method is testing for. So then that means I need room booking, result class on others called the objects results. And say it is equal to whatever book room would return. Which means book room is supposed to return something off this type, which doesn't exist. Of course, once again, I'll just Control dots and generate this type in its own class. All right, so that's done. What's another thing to note is that we don't have this method yet. So this set of requests that was a typo on my part. So this should have been booking request and null booking our book room. I went to control dot and have it generate that method. No, it can infer what datatype the parameter needs to be because all fault we're using. So I apologize for using the wrong variable there. He should've been booking requests. They're not just request. And who is this is our generative method. That looks good, but it is of course not implemented. So let us review what we've done so far. All of this could be considered the range. So we've arranged sample data. We are arranged an object off what we need to test. Then here is where we actually call the method and try and get the results. So that means this is called the OK. All Right. So We arranged then we acted no. We need to assert so we need to make sure that our assumptions are what we expect to happen is correct. And although there are built in, there is a built-in keyword for assertions in exceeded called assert, right? But then there are other libraries and I did see, I would show you one called shortly so you can right-click, jump over to you get packages. And if you search for shrewdly, it would be at the top of the list where it uses kind of fluent assertion kind of style tool, how you can write your code. So I actually like using it's either just go ahead and install it. Of course, accepting any terms and conditions. And once that's done, we can jump back over to our code. So I wanted to show you both syntax says, but you can make your own decision as to whether they liked the library or not. Sorry, want to use the default, that's fine. So I can say assert dots. And at this point it should return the response with values. So that means one I'm most get MC, some response or our result, right? So I can see not null. I'm asserting that whatever object I please in this is not null. In this case, I want to assert that result IS NOT null. If I was using a shortly I could say result dot, but then I would have to add a using statement for the shrewdly. So just see using should link. So I can look at the object that's I would like to put the assertion on and see results dot. And I can see should and look at all of the methods that you get, right. So it should be null or should not be null. All right, It's, I can see it should not be null. This also allows me to put in custom messages so that when I look at hold the test run, I can see exactly what failed based on the message that I am putting in. So it's pretty cool. I do like it and it's flexible. Either way. I'm not going to bore you with the technicalities of the assert versus the should library. Just want to get through the activity properly. So at this point we can assert that this should not be nullified, wanted to get granular. And at the end of the day or assertions are basically based on whatever basis you want to convert to ensure that the code you're testing is indeed properly tested. So yearly stuff assertions might be different from somebody else's list of assertions based on what they think is important. But at the end of the day, I hope everybody can agree on what at very minimum should be asserted or should be covered based on the test. So we could get granuloma. This Those see with the request values, right? So the test name is explaining exactly what should happen. One, the response should not be null, all right, so there shouldn't bear response and two, there with the request values. So that means whatever is in the result should also be what's in the response. So I can assert dot and I can say equal the request. And then sod this point, the bookings or keep one scenario quests the booking requests. I think I might need to just rename this to request. Well, the booking request. Let me just do that stuff making the mistake. So request this control dots and little, rename all of them. There we go. So request dots, full name. And then we can say results. Hotspot result doesn't have any fields in it because it also needs to have the CAM fees that the request has. Right. So at this point, refactor and I made sure that their results on their quest. I like, no, don't mind the fact that I'm repeating these fields just yet. We will refactor and that's why you have the refactoring. At this point. I just want to get through the test, make sure that everything is okay. All right. So I want to assert that full name in request is equal to full name in the results. And I can do that for the other fields. Just duplicate that line with Control D and C, email must be the same as email and it must be the same as if you were using surely. And I'll just rearrange the code here, then you would say something like results dot and then the field, which should be full name, should be. So it should be. There we go. And then inside of that, you put in whatever the expected result is or result should be the same as request thoughtfulness. And that's basically it. So two different syntaxes, two different ways. And at the end of the day, there are other libraries that you can use to kind of have probably more readable or more comfortable looking assertions. That's up to you though. I'm just showing you your options. I like, like I said, this is just my preference that I like whole. The syntax flows when I use the should assertions, right? So for me, I can just duplicate and then I'll do the same thing. Result dot email should be request dot e-mail and request dot d. It should be that. So take your pick, whichever one you prefer. Now, if I look at all of this after a build, I see that it was successful. So I have no syntax errors or anything. And I know have ACT test with actual class is nothing is implemented just yet, right? So no, I can run the test. I can click this little indicator here and I can click Run. So I can do that from here, or I could launch my Test Explorer to run the test from there. Well, it's either just click Run, which will initially launched statistics for. And we see here that we have a failed test. So read tests because of the indicator System.out not implemented exception. So in other words, it is trying to call the method, but it is not implemented. Why is it given that specific exception? That's because if we go over to the method and I just used if 12 to jump over, then we see here that the, it has one test associated with it and it is not passing and it through the not implemented exception. Alright, so when we come back, we will actually write the code here that when we run the test, we should get at least as far as the assertions and then we can see what those look like. 4. Implement: Write Code to Return Correct Data: Hey guys, The last time we were here, we were writing our tests and null, we're coming back to me at this root test, greed. So just as a recap, we are writing a test that should return the room booking response with the request body is That's what artists scenario is. And we have arranged some poor requests, which comprises the fields email, fullname, and requests D it, we have our processor which is not yet implemented, or we have the file, but the method book room is not yet implemented, but we have certain assertions which we saw feel when we run that test. We also looked at the two ways you can do assertions. This one is the natural way that comes out of the box within units. And I introduced a new library called shortly, which kind of brings up until fluent kind of assertion to it. You also have other assertion libraries like fluent assertions, and there are others, but like I said, you pick one and you stick to it if you feel comfortable with it. No problem. So what we're here to do is implement our book room method. Now the test, right, no requirement that we want to write code for is to make sure that whatever is returned as a result has all the values that were in requests. Which makes us have a simple implementation really. So all we really have to do is return a new instance of the room booking result where its fields, full name would be equal to the request, dot the field or booking requests or other dot the field with the same name. And now those duplicate and then do the same thing for date and the same thing for email, there were low. So at the end of this, all we really need to see is that it's returning some object that meets our assertions. Alright, so with that done, let us run our test again. And voila, we now have a green test. So you'll see here it's indicating that one test off one is passing. So our test assertions are no working. So if I was to comment all these assertions and run the test again, you would see that it works just fine. So whether you do assert light this or you use shortly and kind of this style, they will work just the same way. So that's really all there is to implementing code at this point, the stage, at least to have our test, BI green test. So once a break right here, and when we come back, we'll be refactoring the code as our preview off, off why we need to come back and refactor one. You saw that we have requests, then we have results. Both of them are kinda identical, right? That's one tool. We have the processor sitting in the same test project as the test. None of these resources, which are the real resources for the application, should really be inside of the test project. So we are going to come back and we're going to refactor our project and move away what needs to be moved though, if fix all the references and fix up the code so that there's less repetition all around. 5. Refactor: Test and Implementation Code: Welcome back. Let's get our refactoring on. So remember that the cycle is that we write the test, it's red. We write the code to me, the test degree in, and then we refactor if we need to, because sometimes between the two activities we may repeat code or put jobs files where it shouldn't necessarily be null. We're actually going to rearrange a project to haul. We know it's supposed to look and how it needs to look going forward. All right, so what I'm going to do is create another class library. So we'll just add a new project. It's going to be class library. And I have mine over to the side or on, but we're dealing with C-Sharp class libraries. And I'm really just going to name this one room booking up dot core. So this is dot core tests, summarizing the dot core library now. So next, leave that dotnet five. And when that is created, we can go ahead and remove the default class one file. Then after doing that, what we're going to do is take all of the fossa we'd know we need, and I'm just going to cut them and paste them inside of our core project. So remember that from the get-go, we had said that the namespace for the core doctests, the default namespace we had said to be core. Well, this is why so look at that. It's like a plug and play. Everything just went over seamlessly. So if we look at the code files will see that they fit right in with the namespaces as they were created. All right, however, you'll notice that we have a bunch of red lines in the test. It's like it can't see them anymore, right? And for understandable reasons. So the problem here is that they currently have the internal flux and internal means that the namespace, anything within the same neon CSR within the same assembly rather can see it's what nothing else can. So that's why I went to meet them public because well, outside of the testing, we will need to access their externally, so I will make them public. And when we go back to our test, we'll see that we still have a problem and that's because we need a library for. And so I right-click on dependencies and the test project and project references. And then we'll just add that class library click, Okay. And then everything is okay. There we go. Now this is saying that it cannot see this method because of its protection level. So once again, that would be because this is internal. So this meant that public. And then everybody can see everybody and everybody's happy. Now we can go down another root of separating everything are refining the layout of our project. So separation of concerns is not only about creating individual class files, but it's also making sure that the classes are the assets that are related to each other, are located in a central location. So yes, Everything room booking, that's not the artist is in the same project. But then we have what's I could call models, our domain models here, separate from the actual Huan with the mythos and carrying out a process, someone to create some more separation here. And I wanted to call this one processors, processors in kids who have more processors and the throne put them there. And in this also want to add a domain. Or we could call this models, then we call it models instead of domain. All right, and then I'm going to move these over to models. And I went to move the processor into the Processes Folder. Know the consequence of this is that we have to change the namespace. So I'm going to often call this thought models and do the same here. And then for the processor, I'm going to have to call this one Box processors. There we go. Then everything is seeing, it can find the reference. So that's because you have to make sure that we have the new using. So I'm just going to copy this using statement, jump over to our test. And I'm just going to add that using Steven does one as the one for the processors. All right, so now everybody can see everybody else. So if we do one more just to make sure, then the builds is successful and let me just run our tests one more time. I'm just going to right-click and say Run test. And the tests still pass. So that's good. Now, before I move on, I'm just going to show you some productivity tools, some settings in Visual Studio that can help with productivity. One, you can always use this shortcut to run code freedom. So when you have these using statements that you don't want to just click it, hold on Control and press key and then E. But it just removes and fixes it indentation on all of these things, making everything you need. There are ways to customize your own standards for what a cleanup should look like, but I won't get into that null. Another cool tip or two other cool tips that I will share with you, right? No. Involve you going to Tools, Options, going to text editor, C-sharp, advanced. And then you can enable on the show inheritance margin that will come in handy later on as our obligation and expands and we'll have more abstractions and so on. So this really comes in handy, especially when you're dealing with large projects with lots of obstructions. And another one that you'll find useful is the paste. Where is it? At using? There we go. Add Missing using their actives on paste. So that means if you have to copy and paste code, let's say at a copy and paste this block of code to a brand new file, it will automatically add the using for booking requests, which will be that using so you wouldn't have to sit there and try and control data and fill in all of the things that we'll be missing. It does it automatically. So I think those are two good production tools. As you refactor, you want to be as efficient as possible. And when you have these kinds of tools that actually help you to, our help to reduce the likelihood of you making a mistake along the way. So now that we have successfully done our first tests cycle, right? We wrote, I read tests, wrote quote implemented, and we refactored. Then basically we just rinse and repeat. So that's it for the first section. That's our first unit test. But now we'll get into some more of the system requirements are the application requirements and start fishing or more things are tests. We'll get a bit more complicated. And we're just going to have fun. I'm just going to say we're having fun as we go along. 6. Review Full Requirement List with GitHub Projects: Hey guys, Welcome back. Now in this lesson we're just going to be fishing all the requirements for the application that we need to build. You've already gone through our first cyclin. We have a taste for writing the test, implementing the code, and then refactoring. So we know that we have two written centerpiece for everything else, but we need to know exactly what we need to do. So in the repository that I've already created for this. And you can go ahead and create the repository and you can review it using the sexual and source code links. We are going to jump over to projects, someone to create our project in Git Hub, Create Project. And I'm going to name this requirements. Well, let me qualify it a bit more room booking up dash requirements are, I think the description option on at least for null. And we can choose if we want a basic Kanban. Kanban with reviews book, someone to choose a basic Kanban project templates. So go ahead and create the project. And this basic Kanban project comes with a little layout for us to see that we can insert cards with todos and mark-off what is in progress and know what is done. So just for context, the whole weekend kind of replicate this kind of structure. If I click the three dots on this first one and say Edit naught, then we see a preview of the type of syntax that we can use. A C colon colon sparklers in-between gives us those star effects. If you use cool, sorry, asterisk, asterisk and sorrow and texts, you'll get the bold. You get your blood body of texts. If you want a checkbox, you just need your square braces. And then if you put an X in there, then it will interpret it as being checked. All right, so that's good. For nodal. I'm going to remove the other two samples. Someone's going to click the three dots, say Delete, Note click Okay, do the same thing for this one. Delete nodes, okay? And then if we want to add a note, we can just click that plus sign. So the first requirement that we want is one that states what we've already done to say return results with same values as requests, so we can go ahead and add that. So if you want to, once again, you could edit, I could put in even check boxes is one that states what we've already done to say return results with same values as requests, so we can go ahead and add that. So if you want to once again edit, I could put in even checkboxes. So to do that, you want to have a hyphen to make it like a bullet point, then you have your square brackets to make it the checkbox with a space in it, right? So that's open square brackets space, close square bracket. And then the actual verbiage for the task. So when we do all of that and click Save nodes, we see that we have our checkboxes here listed and that we can always just take them all. So we've actually done all of these already. So I'm just, I'm just showing you how we can use the subtract our requirements so you can go ahead and do that. Well, it I'm going to continue adding the other requirements. So I went ahead and just added the others. So you have the first-order return results with same value as our same values as request goes off those new done that's already, the others would be thrown null exception if request is empty. So whenever we are going to be accepting our request for a booking, we need to make sure that values our data is inside of that request before we even attempt the booking. So that's another key requirement that we need to meet. We can save room booking record. So we'll be adding database functionality soon. And we need to make sure that we can do that. Check for room availability before attempting the booking and store booking a record with room identifier, return success flag on results. So whether or not you're booking attempt was successful, we need to make sure we indicate that and to actually add the booking record ID to the results. So those are the things that we hope to accomplish. And you can see I just really copied and pasted the checkbox list from the original one across all of the others. So I can actually just get rid of this some balls. So OK, and now we can move on. So I can just move these across. So this is done. And the next one that we have is throw an exception. It's unfortunate that it's listed in this fashion. And I can't reorder it. Maybe I won't spend too much time on that. So at least we know where we are and we'll just drag them over to the in-progress each time we're about to start the work. So that's I bought it for especially alter requirements, of course, you know, as projects who alone or requirements by James, you have what you call scope creep. And project's view is an excellent way to just keep track off what needs to be done. You can also combine that effort with the Issues tab, which allows it to add new issues, which can be in the form of the group request, new feature requests. You can give them the labels, bulbs, documentation on duplicate first, UDC, whatever it is. So this tool is very, very powerful. I recommend that you spend some time learning how it can be used in your projects. But that's not, that's a course for another time right now we just want to focus on getting through these requirements. So when we come back, we'll start working on the next one. 7. Test and Implement: Throw Exception When Request Is Null: All right, so we're back in our code on wearable to get started with our next test. So I'm just going to collapse that previous one. And then I'm going to put my effect on notation. And this time we're going to have public void. And the test title will be should throw an exception for null requests. So with that in mind, and I'll just review or just remind you what the test 3 looks like. We have the orange, we have the app, and then we have the assert. Knowing this test, it does see that there should be no request or the request value should be null. So I'm not went to IRange and you request object. I'm going to need the processor however. And then my assertion is going to look something like on a Wednesday you should let this time. So it's going to be a good doctor. Throws so you have throw a nail through a sink if we use Async, of course, we're going to talk to she's just been taught us on AsyncTask what others use throw for null. And then I give it the what kind of exception and it should throw. So I would want on argument, null exceptions, argument and all exception is what we're expected to throw because we're seeing that the argument that was passed in to the method was null. So should throw an exception. And then I open braces. And if you just look at the, look at the parameters is C, That's its guide. You are guiding you, allowing us to watch, you can put in. So here I'll do something like a delegate. So inside of these parentheses, I'll make another pair of parentheses with Lambda arrow. And then I'll put in that process are not book room call. But here I'll say null, right? So we're testing if the request those in as null, then it should throw an exception symbol, right? And if you're using the search or the native exceeding its syntax, then this would pretty much the same way except this would say assert, and it would be throws. So you see they're kind of similar. But once again, user choice conflict about always strived to show you the equivalent what I'm using, the shrewdly variation and all problem. So that is artists really, we want to make sure that if the argument is null, that we throw an exception, or I can extend this and actually get the exception that is being thrown. Exception is equal to. And then we can do some other asserts are checks to make sure that the exception.com name so param name would be whatever is common bucking the exception, right? I can see should or should be. And in the parentheses I'll see the word requests. So when that exception is thrown, it should come back with seeing, oh, it was thrown because of this parameter, which in this case should be requests being null. All right, so that is what our assertions look like. Of course, if we do a build, you can ensure nothing is broken. If I run my tests, then we would get or red test. And what I'll do is just adopt the test expert to my right. I think we have enough real estate that we can have it to the right and still be okay, right? So if we look through, we see that we still love the previous test bossing and now we have this new test failing. All right, so I'm just going to go ahead and implement this one at the same time because it's not much work where does need to add something to make sure that the things are never null. So we jump over to our processor. And then inside a form method which now has least one of two tests failing, we can add code to make it PaaS. So I'm just going to say if the request or booking requests rather thus the name of the parameter. So right there, we're going to fail an assertion also what we look at it. So if the booking request is null, alright? And in newer versions of C sharp you can actually write is null. So if booking request is null, then we throw new argument exception. And then for that param name, I'm going to see a name off. So you always want to make sure that you keep your stuff strongly typed, since that'll for acting but the string and add a static string, I'm just making sure to see a name. I'll say if this parameter changes, the refactor is easier. Alright? So let us rerun this test. And I think one of the assertions will fail regardless and we can look at these error messages. So suddenly assert exception, right, param name, and then it seeing should be request bought was booking requests cell is a strand to show you that that would still fail. So where we s, we wrote the test yes, subroutine code that we think works, but then based on the test that we wrote, it is still failing. Okay. Why is it failing? Because we're returning the name of bookend requests. So in other words, where reader returning the string booking requests here, right? But in our test, we did see, let me jump back over to the tests that we should be looking for the word requests. So then I can change dot glassdoor need to change my test. So sometimes the test erodes may bleed it on one path, wouldn't even write in a quarter. Ella is old, maybe that's not necessarily the hall I want to do it. And then you refactor as you go along. All right, So if I rerun this test, I will know get my green ticks are right and my assertions are null in effect. So while I'm here, I'm just going to refactor. So we have processor here. I would have processor here. And for most, if not all of the tests that we're going to conduct, they're all going to be aboard a processor. So instead of doing a new instance of processor every time we have a new method, right? And then that would be 10 different points to update. What we can do is set up a constructor, right? So I'm was going to say CTR tub, tub. And I'll just put in on the score processor is equal to our new processor. So this one line, I wanted to take it. And then of course I need a field for this control dot and generate the field. There we go. So then throughout, I don't want tough to do this arrangement anymore. Right? I don't have to arrange processor every time because now I can do my orange one time in the constructor. And then I can use this arranged object everywhere else. So wherever I had processor, It's still on the score processor. So if I run these tests again, we'll see that they're still passing and know the code looks cleaner. So one instance serving many masters. So that's another week and start refactoring your tests to reduce the repetition in the testing phase itself. 8. Create Booking Services: All right guys. So the next task has all setting up a few things including our service or repository, which is going to store all of our Gandalf, you know, data-related operations. So nowhere looking at more separation of concerns because we want to obstruct the database kinda stuff from the actual business logic, the if statements as to whether or not this request is OKR and so forth, right? So what we're going to be doing in this one is setting up our eye room booking service. So I edited that save room booking record, naught and I added this extra task. You can do that if you wish, and also move the last one or the previously created one over to the completed pile. So let us jump into the code. And in the core project I'm going to add a new folder that I'm going to call services, data services. And in that we're going to add a new, I'm just going to say class, but it's really going to be an interface. And I'm cleaning it to i room booking service. And we can add that. We change this to public interface. And in this interface we're going to have a method that will return, well, it doesn't need to return anything. Yeah, it's a save method so it doesn't return anything, at least not retinal. So it is void, Save, and it will be taking parameter for the room booking, right? So the thing is that up until now we've been dealing with requests and results. However, when we're saving, we will need to see like a database ready version of the request. And that is going to require us to introduce a new type. So I'm just going to put in a new folder. I don't want to call it the mean at the models and love the domain. And then instead of domain, we'll add a new class. Well, that's fine. That was the step one by one, right. So I went to call this room booking. Right? And then it is room booking. So this is our new data type that we're going to need. So i room booking service and it's good to have a method to see the room booking and it expects to get something off datatype room bookings. So well the way up to transforms off the values from the room booking requests, whatever it is. So if you've done application design, you'd appreciate that sometimes you have to massage the input before you can send it to the database. So that is what we're preparing our code for by separating these null, I need this room booking some, just going to create this new file by itself. But for now it's going to have the same fields as the requests. Alright, so I'll just go ahead and copy and paste those. And you can see that extension on R-dot function that's add encourage you to enable recently at work, right, when I pasted it automatically added that using statement for me. And he Whoa, what I'm going to do is move this out of the data services and put that in the bill mean. And then I'll change that to domain of course, and update the reference inside of the interface. So that is at least step one to just preparing for next move, which is when we're writing the test, we'll have the interface ready for use. And what we're going to be doing is making sure that we're saving our desk booking requests. And that is what we're going to work on in the next test. 9. Unit Test: Save a Room Booking: All right, so we're back in our test code and the next test that we're going to do is want to ensure that we actually save the record, right? So I'm just going to copy all of this and reward accordingly. And this test is going to be called should save room booking requests. All right, now inside of this, what we need to do is call our processor. Those of course, we need to call. And we're calling book rooms so you see how much easier that is done. We know the processor is there for you so you don't have to initialize it again. And inside of this, we need our request. No. We have no requests. Would have to arrange another request, but we did already kind of arrange our request up here. Right. So what I'm going to do is follow suit. I'm going to take this out of the arrange for this first test that we did. And I'm going to put it inside of the constructor. And instead of having a local variable, I'll make it into a field. So we generate the field on the score request. And then anywhere that said only requests we just replace with underscore request. This request represents our tests requests and an artist requests can be used anywhere we need it to be used since that offer beating this object once again, put it in the constructor, and then we can just reuse it throughout artists. So now that I have done the Irene's more global leads that we can actually just jump straight to the right. Now before we can do any assertions here, we need to set up, copy our tests ready version of our interface. The reason we did the interfaces so that it can be injected anywhere we need it. But it is still an interface that needs to be set up because it has a method. We need to see under testing circumstances host should this method works. So that's what we call mocking. So thankfully, there is a very good library used for this kind of thing. And we're going to see how that works null. So I'm going to say underscore room booking service mark is equal to new Mock capital M or CK off our interface, which I'm just going to quickly copy that and paste it automatically getting the using statements, that's so cool. And then close braces. And then for the mock, it's obviously not something that we have just yet. Would we control WC install package, mock, say can go ahead and let NuGet, find and install the latest version. And when that's done, we automatically get the using statement. And then we can initialize our field by the name room booking service mock or. So. Notice it's off the type that we want, but it's also MC. So we're saying give me a mock object off the datatype. However, this object is still kind of empty. We have to do what we'll call the setup. So we're still in that range phase. And now we have to actually sit this method to do what we think it should do before we get there though. More importantly to me is the fact that we need to inject this service into our processor. So if you're not so familiar with what injection or dependency injection is, I suggest you look at one of my previous courses on ASP.net core development, where we show what dependency injection really is and how it helps. Now at this point, this is a service that the processor is really going to use after it finishes its business logic on its checks, it's going to use that service to effect whatever actual change needs to happen upon receiving the requests. So that means it needs an object of booking service. It has not been prepared for that just yet. So what I'm going to do is take this processor declaration and I went to move it. So after we have the Mach, then I went to tell this that it should also take a parameter of our mock object. So when we say mock, it is literally the type of whatever type. But we want the actual concrete type. We have to see the mock objects. And if you hover over object, you see that it's actually giving us about the eye room booking service. The room booking service is actually the MAC off that. So we're getting the physical value. Now the red line, we can address this by saying controller.js and then we can tell it at the parameter to our constructor for us, right? So that's really reducing the buck and forth between the code fast. They see no, The red line is gone. If we jump over, it is no getting that object, we'll come back and rename that unreflected that soon enough. But right now I'm just showing you what we're preparing for because in all we can inject the service into our requests processor and then we can use anything out of the service inside of any method inside of our processor. So back to our test so far or our range has us with the test requests. Then it has a setting up the Mach repository, our mock service. And then we're setting up our processor object, which takes that mock object in as its parameter. Knowing our actual test, I need to let the mock know how it should behave. I could have done that up here. But then that would've been global. And only one method really needs that kind of setup at this time at least. All right, so let's see what this entire method is going to look like. So we still have to do some arrangement. And I'm going to do that above the act. So inserts or the optimal, we, clearly things have changed. So let us put in place what we need. So I'm going to have my object of type room booking. Remember that we just created that one in the domain. So room will king is equal to null. So it starts off empty. Then we're going to see our mock object. We can say MLK dot setup. There we go. And this takes a lambda expression, I'll just use q. And then we can call the method that we want to set. All right, so in other words, when this Mach is involved, how do you want certain methods to behave? Watch about method2 in a test circumstance, that's what brings to the table. So I went to see the save because that's what we're about to tests, right? When you are called, You are I'm setting setting it up to know that it should take any so I'll see it is any object off the request booking. So room will King requests type. There we go. All right, and then close parentheses. So that should work what I'm seeing our red line. And it is because I'm using the wrong data type. This should be room booking apologists, right? So those are going to save the domain objects, right? So this should say Save, it is any room booking. So it's saying give me any object as long as it's soft diet, room booking. Right. Then to continue, I'm going to do what you call a call buck. So in other words, when this method is called, what should it pretend to do? So the callback method is going to say R. It gives you the datatype that's I should quote unquote return or process. When this is called. You can read the documentation if you need to. But for the call buck, we're just going to see booking and then a lambda expression. So it's almost like a delegate function really. And then inside of this, we're going to see that the room booking object here is equal to the booking object here. Maybe I can quantify these names a bit more. Someone to see. Saved looking. All right, so the Save, the booking object that we started off as null should know, get the value of whatever was being passed in during the test. Alright, semicolon, semicolon. And that is our setup for our mock, or the setup for the save method in our mock object for our service. Alright, so it looks complicated, but once you start doing this more and more, it will get easier and it will make more sense. So after we've done that, then we actually call the processor. And then after we call the processor, we need to verify that the method was called someone to say mock dot. Please verify that. And then our lambda expression, Save or Save Method. And we're verifying that it got one, The any booking object. And then two, we're going to verify. Right? So verify that, save with it's any booking object. And then the next parameter, if you just hover, you'll see it in the experimenter would be the number of times it was called. So you can put enough fail message, you can put in different things. But what we want to verify is that it was called 1 times 1. Alright? So in situations like this, what you can do is say when you're calling a method or when you carry out this option, verify that certain key method's along the way were called once or twice. Say if you're testing that with this input, this method should be called twice, or if it should be called thrice, et cetera. You can see once, you can see exactly once, at least once, right? Most, least, so to speak, right? So you can, It's quite flexible with the times that you can use. So once again, we're at once. I don't want to know if it was call it twice, then I have an error. All right, Now, after that, verify, we can go ahead with more assertions. So we can see assert that this is not null. So save Booking dot should not r. Let me just null. Sometimes it's, It's easier to just the word non-analytic filter that's olds. So it should not be null. All right? And all the assertions would kind of be similar to this. What we did up here, where these should kind of be equal. So requests dot full name. So CVE Booking dot full name should be same as the request flow name. The email should be the same as request email and the date should be the same as a date. Alright, so those are my assertions. I'm going to run just this test and by right-clicking it. So in the test expert seeds they're on it. Not wrong. It doesn't ever been ruined, so it doesn't know if it would ruin. And then I can just right-click it and say Run. And in Russia and it has failed. And if we just take a look at what did you see, it says the expected invocation on the mock once bought was 0 times Save. All right, so it saying we expected that this should be called at least one. So whenever we did the act, but it was never called. All right. So that's all the MAC verify works. So no, we can go ahead and read, sorry, we can go ahead and implement the code to make all of this called into life. 10. Implement: Save a Room Booking: So back in our processor, we're going to put in what we need to make our test agreed. So we see two out of three tests are passing, right? And we have to write the reminder of what the test failure was. So sometimes it's good to just have this up so that you can read the message and then look at your code at the same time. Sometimes, you know, just having them up at the same time can help you to surmise what you need to modify. So at this point, what I want to do is clean this up a bit. So I need to finish this injection, right? So Control dots the proper using statement for this. There we go. And I went to call this object's name itself our room booking service, control dot, and allow it to create an assigned field. As, as you know, I prefer to use the underscore for the field names. So I'll just change that to a unary factor. And then pretty much what we need to do is introduce the of. So one, we get the booking requests. If it is null, we throw the exception so we know about that. But then in-between the throwing are the checking and verifying that it's okay and returning, we need to save. So if it's okay, we need to save. So what I'll do here is just say room booking service, dot save. And then that takes a new room booking objects. So just say new room booking. And I don't like seeing those prefixes refer to put in the using statement. So room booking. And then this object needs data which pretty much is the same data that would have been present in the booking requests, right? So I'm actually just going to copy this and paste it there. All right. And there we have it. There we go. So now we have our room booking being saved or save method being called. Getting any quote unquote, any room looking object. And know if I rerun my test, then I get up passing score. So easy that was no. Clearly, lots of repetition is going on here, right? Between this line are those few lines, those few lines and the similarities between our booking request and our result on the actual domain. There's opportunity for us to kind of clean up hallway approaching, spreading out all bits of code. So let's start off by seeing the similarities and see how we can condense, right? So we have the room booking with these three fields, then we have a request and the web results. Keeping them separate is important. However, we want to reduce the repetition between all of them. So we can introduce, I'll just copy and paste of one of the room booking results. And autumn winter hub here is room booking. Base. Room booking bys will represent and just Control dots. Or rather I need to rename this file, sorry, so just rename the class. There we go. So we'll have room booking base, which has all of the objects, right? And I went to meet this abstract because I don't want anybody to be able to create an instance of the room looking BCE. But it is going to serve the purpose of inheritance across all of the other ones. So I can remove their overt declarations off the fields that they have in common because now everything exists in the base. Alright. So the same for room looking, I can remove this and let it inherit from the room booking base. Now if I do a build, everybody's still happy. That's good. So that refactor work. All right. And like I was saying, when you initialize the inheritance margin, you'd be able to jump between files under base implementations much easier, alright, or much more easily. So that's what this inheritance margin is. Four, if it works for you, if you find it cool, then no problem. If not, then that's fine. Now, when we go back to our processor, we have an opportunity to make the creation of these objects. I'll be more generic. Because you see here we're really just repeating code. And they're both hinging on the file is coming from the booking requests. So I'm going to create a private method that is going to return the datatype T. So if you've ever heard of generic tea or tea room booking, right? So tea room booking, I'm just calling it generic. Create. Room booking object. Or I'll just say create, yeah, room. King object. I'm being explicit with my method name, that's all. And then that is going to let it know that it is a generic, right? So we're just call it T room booking object, create room looking object is the name of the method and it's generic parameter is whatever it should be returning. It is going to take a parameter of the room booking requests. So I'll just copy that, paste it there. And then I'm going to unpin this so we have a little more realistic for this activity. And then I went to specify where the tea room will King object is off type room booking base. Right? So in other words, I am willing to be able to pass in any are called this method and I have to pass in a parameter or let it know of the generic being relative to the datatype room booking be so anything inheriting from room booking bees can be used as a generic. Alright? So after doing that or implementation is basically just going to say return an object of tea room booking. And pretty much it's just going to have the same fields as full name. Throat, email. Sorry, I should've put that new keyword return, new tea room booking, semicolon. And I'm getting an error, kinda create instance 0. It does not stop the new, sorry, that should have been new, the end of it. And there we go. All red lines are gone. All right. So I guess I could shorten some of the the names, right, so that it doesn't stretch so far on the screen multiplets I'll do is just break the line so you can see all of the code. So all of that is the first line. And then we open the braces, and then we do our implementation here. Alright? So the purpose of this will be to replace our explicit declarations here is instead of seeing new room booking and repeating that I can know, say creates room looking object. I want something off type room will King, which inherits from the base. And I'm passing in the booking requests as the parameter. There you go. All right? And then I can do the same thing for the returns. So I can just say return creates room looking object, but it would be off time rule, the type room booking results. And there we go. So that looks much cleaner, much less repetition in between the two. And we see how generics can help us to keep our code clean. And just stayed the suggestion from Visual Studio to me at the method static. There we go. Now of course, the moment of truth is three tests, so I'll just run all my tests. And I'm getting green ticks all the way. So that is it for implementing that test. We did a refactor and we did the retest. And once again, we're just going to continue rolling through these activities accordingly. 11. Unit Test: Check if Room Is Available: Well, we're equipment. Oh, nice. So we're on to the next activity and that is the check room availability before attempting the booking, right? So we need to add some logic to make sure that we check and see if there are any available rooms. And then if there are no available rooms, then we do not routable key. And I send symbol, right? Of course, doing the bookkeeping is the same as saving the bulking. So let's jump over to our tests. And I'm just going to do the regular copy and paste and rename. And this one is should not save room booking requests if non available. So inside of this method, we need to do a few things. Before we get to that method. We actually need to create some new methods inside of our bookings service. So let me jump over to the interface. It doesn't implementations, so sorry, that was Control F 12, you should have, you can use leftover, can control-click. And over here, I'm going to say, give me a new method that returns. I'll use I innumerable, I think that is a generic in our collection types. So give me a collection of rooms. So I need a new object called room. And I went to call this, get available yet available rooms. All right. No, This method is going to have to take the request the right. So I'll just say datetime did. And all we need to create this new objects. So creating a new file. And of course, this room objects needs to be in the domain, right? So I'll just move it over, change the namespace. We don't need anything in that object just yet. We just need to know that it's there. And our interfaces happy gets available rooms is okay, that's fine. Now inside of our test with our setup for our mock, we need to let it know that it should return. Well, in setting up the Mach, I need to let the available disk method know that it should return the list of rooms. Now at this point, I'm going to, okay, I'll leave that step one to the refactoring stage. But what we're going to do is pull this kind of setup, setup inside of the test. What we did at such a later date. So for null, let me just go ahead and do the same kind of setup an IV case, you just want a reminder, you can always look at the previous setup where we did the setup for our safe. So we need to have a list of rooms. I'm going to create a field brevity list of type room for this stuff type room. Go ahead and add the using statement, and I'll call this available rooms. So if it's privates on the score available rooms, right? And then inside of our constructor, I'm going to initialize available rooms to be a new instance of lists with room, with at least one room in it. So new room objects, at least one. So that way we know that it's not empty. So in the setup, we're going to say OR room booking service, mock dot setup. And this time I'm going to be calling on the available rooms method, which is going to take her request it as its parameter. And notice in this situation I'm using an exact value. I'm not using the arbitrary. It's not any room looking because we literally need to test against the requests, needs that we can change at anytime. So setup yet available rooms. And then for this one it's a value returning. So it's not avoid like the save where we just had to carry out on oxygen. So this time I have to see this BreakLine dots returns. And then it will tell us what it is expected to return or watts delegate we can carry out, but I just needed to carry out return with the available rooms lists. We had IRI and previously knowing settle for a test, having done all of that arrangement prior to this, what I'm going to do is clear or available rooms because remember that it should not do the booking if there is an unavailable. So if available rooms is the point of reference, then when I'm running this test, I'm going to make sure that there are quote unquote, no available rooms. Then I'm going to call my processor to book room with the request. Alright? And then I'm going to verify, and I'm just going to copy and paste. Little wrote all of this before, so I'm just verifying that this is never called. All right. Nice and simple, right? So we go ahead and we see once again available rooms. Yes, we have a list of available rooms. We have a list and it has at least one room in it. So we know that we can test for available rooms. But then this case, we want to say it shouldn't do any booking if they're non available. So I'm going to simulate no booking rooms being available by clearing my list. And then the processor is going to make its regular call. And then we're just going to verify that this is never called. At this point. This is acting as an assertion. I don't need any other assertion because I don't need to verify that any values came back or anybody's are the same as anything else. It's just going to see that it is never called. Alright, so let us test this new one. And we're read and I think we're good to go. So lets us jump over to the implementation of the code to make this happen. 12. Implement: Check if Room Is Available: All right, So we have already test, let's jump over and do our modifications to make artist pass. So jumping over to our requests processor, the condition is that if there are available rooms, then we can go ahead and see if there are no available rooms, then the save method shouldn't be called. Someone to start off by getting the available rooms. I'll say var available rooms is equal to my room booking service dot gets available rooms for the current request bit. Doing that, I can see if the available rooms dot cones. So this is returning an innumerable SF to say cone open and close is greater than 0. And this requires link. There we go. So if the available rooms dot cones, and then here it's seeing that suggests on these that I could see adopt any, right? So if there is any, which would just give me back a Boolean. If there is any, then I can see. So with that, if condition inlet us three tests and our test is passing, so if there are none, then this would never get called. All right. Or if there is no available room that wouldn't ever get called. All right. So we're going to do two activities. No. One, I'm just going to discuss briefly what I mentioned earlier about probably refactoring or rearranging how we do our setups. So in the past I've worked on projects where instead of setting up in every single method, for every single method as it is needed. What we did was we created single file that just returned the whole mock with all of the sample data, all of the setups, everything. So we have 1 of contact. If you need to do a setup, if you need to do anything related to the mock object, we have 1 of contacts and all that is relative to how you're interacting with the data. Of course, because in our situation, before it's a try ripping these things built, of course like rooms. You could build a pseudo list of rooms elsewhere, but the available rooms is being used by other places. So you'd have to take that into consideration. Also for an ad to settle for the CFD booking. We have the syllable gain here, but we are doing some assertions against it. So why they would seem easy enough to just rip all of this OTA and just do the setup in its own file. And then we would get the save method already set up. Whenever we get a mock object through that file, we have to consider some of the assertions on the dependencies, right? So with that in mind, I'm not necessarily going to go ahead and do any refactoring with the tests. I just wanted to point you told if you see the opportunity to do that, then you can know the second thing that I wanted to do is to show you how you can debug. So sometimes you may write a test that you think is working or should work. And relative to the code you wrote, at least the test and the code should work together other and for some reason the test is failing and you're not entirely sure why. And it's probably telling you some MRSA that's not necessarily to seeing what the error is. So I made an error or I just modify the tests that we wrote and modified it so it can fail, right? So you don't necessarily have to do this activity, can watch what I'm doing. This, they can understand this works. So I made the testfile, right? And it says that they inspected invocation on the Mach is once, but it was never called. So then that skewer, I'm curious about that. My test says it should call it 1s, but based on the quarter ROTC recalling it given the test scenario. So of course I want to know at what point is my code doing something you shouldn't do. Sometimes he can be artists, right? So you just have to make sure you check both sides. So I'm going to write the test itself and then say Debug. And before I debugged, actually put a breakpoint somewhere in the method, right? So I should have said that earlier. So before you enable, you want to put your breakpoint. So regular breakpoints like what you would use when you're regularly or routinely be building something in your code, you can use those. And when you go into debug mode with the test, it will actually acknowledge them. So it will go ahead, her relative to the test scenario, right? So if we look at booking requests, you'll see that this object is exactly the test stuff that we had mocked in our test class. If you look at available rooms, it's not null. The requested is that if I step, then you see available rooms is 0. Why? Because we cleared them before that particular test was run. All right. Then I say if available rooms is any, then it should go and say if it is 0, if the current is 0, then there is none. It skips over it. And so it never cause it's as far as I'm concerned, it's doing what it's supposed to do. So whereas the ECS, I continue and then it jumps to the line note that history and the error, seeing the inspected invocation is ones what was called 0 times. And then I realized, oh, my invocation is wrong. I should not be checking for it to be called once. I should be checking for it to be called. Never. All right. Well, I'm just showing you how the debug mode can help to 0. It's maybe out what points of your code it's doing something where it relative to the test or at what points off your test, you have an error. All right, So you have to make sure that these two work in tandem so I can stop debugging after making that change to the never, you already had that so you don't have to worry about it. But when we rerun this test will see that it no passes. All right, so that is how you can go ahead and add a little debugging to your tests as you go along. 13. Test and Implement: Add Room Id To Result: All right guys. So for this activity, it's not going to be a long activity is really just an addendum to something that we've done already. And I think we can do both test and implementation and refactoring all in one sitting in this situation. All right, so the next requirement is that the ID of the room that is booked should be present on the room booking record. So it's really this an addendum to when we know we see the room looking record, we need to make sure that there's an ID prison. I'm jumping up to our are available rooms list and I'm going to put in the field for the room object. It doesn't have to exist. That's fine. But ID is equal to another, say one for I would just want to test method. So again, this control dotnet it get the property in jump over. And you see it's already an integer, public integer. That's good. All right, so now we have at least that ID in for our, whereas it should throw, should see, there we go. What I'm going to say is that the saved Booking.com. And at this point there's no disk ID on the room looking either at. So room booking room booking base, not the result may be the result, but not the base. I don't want it on the base because the request wouldn't have any room ID. Right. So I'm just going to give it a property, say prop top tub. And this is going to be room ID. All right? So I'm going to say saved booking, room ID. It should be, and it should be equal to, in this situation, the first ID from the list of available rooms. So I'm just going to say it should be on a square available rooms, dots. First, I want the first object in its control dots using link first dot ID. Alright, so once that is called, then when we are verifying all of these things, that room ID should be the same as waterhole was the first available room in the list. Now, let us run the tests. And sometimes it's difficult to see are remember which one he can always just click the tick and say run it. All right, so we'll just do a project build as always. And then he can watch statistics floor and you see here that it's failing. So it was that one. So it says that based on mass or Sean deceived booking room ID should be one bullet was 0. So that's nice. A nice English whale seeing what's his wrong or right? It should be one but it was 0 and that's fine. That's expected because we haven't put anything in place yet. So that's the modification to the test. Let's jump over to the processor, can remove that breakpoint. And then what we're going to see here is if there are any available rooms, which we know about a time it's running the test, there will be at least one. Then I would want to see, get me the first one in the lists of our room is equal to available rooms. First, the first one in the list. And then I'm going to take this method call out because yes, I need the room booking object, which is really doing a fantastic job of all of this. But the end of the day, I also need to put in the new room ID, which would be here. So I'm going to say give me the room looking requests by itself. So var room booking is equal to create the room looking objects. And then room booking dot room ID is equal to the room that we've got. There we go. And then we save or room looking. Alright. So with all of that, Helen and let me go back and rerun the tests. And now we have green. So busy sometimes requirements coming in already miss something. It might have to make an addendum to an existing tests and you tuck everything on as you need to. So that's really it for this activity. There isn't anything much to refactor, anything at all to refactor. We just modified what one of the assertions and modify the code to make sure this assertion holds true. And that's really it. Just modified our test data and this requirement is no fulfilled. 14. Test and Implement: Return Successful or Failed Booking Result: All right, so our application is coming down nicely. We've been checking off all of our tasks as we go along. And so we have a few layers. So the one that we're going to be working on no is return success flood on results. All right, So whenever we submit a request with the results that is returned from the code, we need to see a flower as to whether it was successful or not. No, jumping over to the test, I've already made the method stub. And before I move any further, I'm going to show, you know, the concept of data-driven tests. So data-driven tests allow us to actually, instead of mocking and staging data within the method, we can actually put in parameters and then we can feed them in. So the first step to a data-driven test is to not use book to use theory. Alright, so that is our new annotation. And then we have inline data, which then takes us many parameters as we would be providing for the test. So in this situation, we want to test a scenario where we have a success flag. And I could extend this and say success or failure flood. All right, because we want to indicate whether the booking was successful or not, and I'll be using an enum for that constant. So I could say booking, success, flag. And the type doesn't exist those yet, those do that. And the next parameter would be, is available. So we'll want to know, is the flood this value? When it is that value. So pretty much that's the data that we'll want are the scenario that we want to posit is based on these two values. So before I move on, let me just create this. I'll just create that in models. Or you could create it didn't domain, or you could create another folder. I think I'll just create another folder and we'll call that one enums. Some know for sure whether those are going to be. And then I'll just add a new class called it booking success flag at that. But it's really a public enum. And this enum is going to have to What is success and failure. All right? So it's once again just stuff like the sea. It was a booking success or it was a booking failure. All right, So no, I can jump over to test and include the missing reference for the enum. Alright, so for the inline data, I can see bookings, success, flag, failure, coma. So it should be failure when this is false, right? If there's nothing available, then that should be false. And then I can see success when this is true. So these are presenting data scenarios, so the test beforehand. So whatever code we write it will automatically infer the scenarios based on what we're providing here. And of course, the parameters provided in the inline data will go in accordance with the parameters all aligned for the method. So for the test itself, I'm going to just write code that's a generic enough for either scenario. If want to test when this is not available, I'm going to want to clear my available rooms, right? So this is our test data. And of course if I'm testing for when it's not available, then I want to replicate this line. So let's see. If not is available. Then carry out this directive. Clear the available rooms. All right. Then I'm going to say var results, which we know we get back when we call our processor. Book the request. So I'm just going to copy and paste that line. We know that it returns our results. So we barely are rarely tested it are there situations where we didn't have to get the results for our assertions and there were situations where we had to get credit. That's fine. So in this situation, var result is equal to call the request. And then I'm going to see that the, the booking success flood dot should be and it should be at any point equal to the code. So r to flood the incent buck by results. So flag does not yet exist, so I'm just going to Control dot let it generate the property inside of that result class for us. And then what we can do is jump over there and change that property and make sure it is what we expect it to be. And there it is, public success flag. Alright, so that is now a part of the results. I can see that the test is going to infer or assert rather that at any point, no matter what the situation that it's being presented with. If it is not available, we clear and then we try and get the result. So the booking success should be equal to that. Whatever is provided on this edge should be in accordance with whatever is returned from the results. So I'll just do a quick build just to make sure that everything is okay. And once you can verify that, I'll just run this. And artistic explorer lets us know that that failed and that's fine. So if you notice when I dropped on this this carrot, it's showing two nested tests under the one method stub, right? Showing you the test first scenario. So the first scenario, which is the scenario where it should return success. So by default I haven't done anything but the enum value is always going to default to the first one says defaulting to the success. All right? And then the failure scenario is not being carried out because you see here booking success flag should be success. Both was failure, right? So now we need to put in code to make sure that all of that works. So in our processor, There we go. We need to bolster this if statement a bit more. So pretty much I would have to let it know that the result, remember, returns the results. So we go and build the results manually. Don't hear. Well then we just added that flood. So we need to be able to determine when the flux should be, what value, right? So I'm going to, instead of returning right there, I'm going to build the result from or to someone to say result is equal to. Because regardless of the outcome, we know that a result needs to have these values. So we know that moving this refactor shouldn't play a huge part in making the code behave differently. However, at the time that a booking is about to be saved, we know know that after it has been saved, I can see that the results dot flag is equal to booking, success, success. All right? And so, so if there are any available rooms, we do what we did before and we set the flag. Otherwise, we are going to tell it that it failed. So the success, sorry, the flag will know, say failure. All right. Calling this booking success flag is kind of misleading. So I'm going to change that. So instead of booking successfully, I'll call it booking result flag. That's more generic. So this control dots and let it update all the references all over the code. And that looks much better. So at the end of this operation, we will just return the result. So we built the results from early and then we did our business logic. And then I put this after the saved because if this field then clearly that could've been a success. Otherwise it was a failure. And then we go ahead and return the result. So if I rerun that test and I'll just do from the mean stub, then I'm getting green ticks all around, right, nice and easy. So as we put in our tests, we implement, and more importantly is the fact that we can do theories or data-driven tests where we provide the scenarios. And instead of writing you statements to say if this then that if this then that statistics, a whole lot of things we can write one test that will fit into the different scenarios that we expect to come out of our method. 15. Test and Implement: Add Booking Id To Result: So we're back for another exercise and this time we just wanted to add the room booking ID to the result. So up until now we have the room ID being added to the result, meaning yes, I booked this room. However, we don't want to have the ID for the record that has been booked. All right. That's fine. So I already have my methods. Notice no annotation because I really want this one to be data-driven. So I'm going to have two parameters. I'm going to say a room, booking, ideas, an integer. And based on if there is something available than that value might change, right? So I'm going to do the same thing here where I have Boolean is available. So I'm going to reuse my theory. And I'm just going to copy and paste the two in lines. What obviously the data login. So let us say what lets us look at what happens. I would expect that maybe an ID of one would be therefore the room booking ID when it is true that something is available. However, if nothing is available, how would expect nothing for the ID value? And this would be false. Oh, I love the change my datatype here because I'm seeing null, but this can take a nose after make it nullable, right? So they'll split our question mark there that makes it nullable and then that is fine. So once again, because of my scenario, if there is nothing available, then clear the room. So for the scenario where is available is false, then clear the list of available rooms. Otherwise, I want to set up my save method. So I'm just going to jump right back to where we tested for the sieve. There we go. I'm just really going to borrow this. I'm not going to retype it down, just boring that whole setup. All right? And instead of all of what I did there, I'm going to save Booking dot ID. So I went off to add this ID parameter to the booking class because obviously there's no ID for it yet. And this ID should be equal to whatever I'm passing in the room booking ID, dot value. Alright. So I'll just Control dots. Let it generate that property. And I know we'll have the idea and room ID. So if you're meticulous, you may want to rearrange this and put Idea above the room ID. That's fine, but it won't make the code work anymore or less. It's just a matter of readability, but that's fine. I'll leave it as it is. So that is the setup for the C when we are testing for the scenario, true. So if it is not available here, the rooms, otherwise, go ahead and setup on. Wait for that ID being equal to value. So after all of that is done, I also need to make representation in the result for the room booking ID. So I need to have public int room booking ID. Right. Now, our OCT on assert in our test method will be similar to what we had previously where we're carrying out the call for result, and then we're asserting that the room booking ID of the results should be equal to the room booking that has been passed in. Now there's a problem here and it is seen that in does not contain a definition for should be. Okay, we know that that's not true, but I think it's just a datatype in issue because this is an nullable int, whereas this one is just int. So let me make the room booking ID nullable. And there we go. All right. So I'm making that nullable because in the event that something is not available, then the ID the booking ID is going to be room booking ID is going to be no anyway, right? So I'm just making nullable that a result knows that it doesn't necessarily have to have a value at anytime. So let us do the red tests. So let me run. I didn't notice it saying that there are two scenarios that haven't been run yet. Once again, these tests are seen as multiple tests within one sitting. All right, so let us jump over to our code. So this would be in the room booking processor and PR too much. What we're going to do is append to this area. That results dots. Room booking ID gets the value of whatever room booking dot ID becomes. All right, so at that point it gets that value. So you see that that test.py was testing the null scenario head. Let me jump back over to the tests. So it was parsing for the null scenario where this is null and that is false. So that actually would have happened that night Charlie, because here we're not sitting any value for the room booking ID. It is nullable. And so null is automatically being a center is all dot room booking ID in this situation. So we need to make sure that this one passes. So when we go back and run our tests again, then we will see that we're getting green test. Alright. So know our assertion that the room booking ID, that's his return should always be similar to the value that was provided. So at least one is provided, then the room booking ID is coming back as one when knowledge provided it is going back as an all. So that is good. So let me just do a build and we have nothing else to do no major changes, so we don't have anything to refactor too much. So that is it for fulfilling these requirements. 16. Section Review: All right guys, so we can confidently tick off all of the checkboxes, pull over all of our cards that don't. And we see that for these preliminary requirements, we have implemented tests and we've written code, and we have good code coverage for the core functionality of our application. So all of that is done for now. So we have completed unit testing for the application core. No, of course there's much more work to do. When we come back, we will have to finish the implementations for our interface methods, because clearly, there's nothing really happening here. And right now it's still in theory. But the point of test-driven development is that you can actually get a good idea of the API design, the overall application design, or the business logic design. Before you actually get into the meat of what really needs to happen, you can wireframe all of your classes should look and how they relate to each other from very early on. So that's what we've really been doing. So later on we'll look at connecting to the database and writing unit tests. Verify that database operations are behaving how we would expect them to, and then escalate that into testing the full application. But for now let us just tricking our changes. And my commit message is finished unit testing application core. We can commit all and sink. And when we come back, we will look at more activities. 17. Refactor Project for Data Access Layer: All right guys, welcome back. In this lesson, we want to walk through the steps to set up our data access layers. And this is going to involve some refactoring and rearranging of our projects. If you look at my solution explorer, you'd see that I have a few new projects and some files are in new places. And I will want to walk you through them step-by-step. I didn't want to do it on camera because we all know how to create projects. So I'm just going to guide you and you can pause and replicate as we go along. But as long as you just look through and do it step-by-step, they shouldn't have any major problem. So let us get started with the new projects that were added. We added the Up domain, so room bulking up dots domain, that's its own project. We also added room booking up dot persistence of both of these, I just class libraries. And if you remember, the room looking up about core was only a class library. So the same rules that you use to govern the room bulking up the core, you can reuse those for the domain and the persistence. And then we added another x unit test project room bulking up dot persistence. Right? So you can go ahead and add those three projects to your solution. Now in terms of the rearranging of the files, what we did was to take the domain folder or file server in the domain folder and place it inside of the domain projects. So I got rid of the folder not before though. Of course, I caught the files and paste them inside of the domain projects. So coatings as easy as clicking the file you want and you using Control X go into the project you want and saying Control V. So that's all I did to cut and paste them into the domain project. The next major movement was the movement of the room booking base, right? So remember that we had the room booking base class itself, the models folder. I took that out and put it in its own folder in the domain class called base models. All right? Now, of course that would upset a lot of what we have in place because the namespaces have to be changed and we have to put in additional using. So the firstName space changes occurred with the files that were moved. So room, room booking and room booking base all got new domain. Sorry, namespace names in the form of room booking, dot domain For room and room booking, and then room booking this room booking up dot domain dot based models. So with that change, actually lift some of the errors that would have been expected. So you have to firstly let this project, nor that it has a project or a reference to the domain project. You can do that in one of two ways. Either you go, I use Control dot and you'd actually see this addition to add the core reference. But if you wanted to take matters in your own hands, you can always right-click dependencies, add project reference, and then just make sure that you take the mean. So once that project reference is present, we can now control dot and add the using statements anywhere that got offended by the change, right? So you can just go through each file meticulously and make sure that we're updating the references on others. Use Control key to clean up as I go along. And in our processor would know that this would be a very upset file because that's where a lot of the action took place. So just use Control dots that are using statements. And anywhere that is not satisfied. Just control dot, Control dots. And with a few, it should be, everything should be okay. So just make sure your namespaces are correct and you update all the references as you go along in the test. So I'm sure we're going to have a few that we need to update also. So control dot and update the new namespace references. And with one, everything is fine for me in this test. So that's the surgery with the domain. All right, so you can go ahead and set up this persistence president and all by adding a folder called repositories, which is empty, right? No wearable to fill it. I'll do that one together, but we'll do that one together. But for the DB context, what I've done is to add a reference to Microsoft as Entity Framework Core dot SQL Server. So you can either put it in the project for the XML or of course we use new gets you just right-click. Go to Manage NuGet packages and you can browse and search for SQL Server and install that library. So once you have that library installed. When we're setting up our DB context. And I still have some offending vote here, no problem, Control dots and add the using statements. Of course, the persistence projects will also need a reference to the domain project. All right, so once again, just make sure you have that project dependency. So if the control Dot didn't suggest it's are you want to do to self, he can go right to hit us for the rest of the file and see what we did here. So one, we add the reference to the using for Entity Framework Core after we let our public room looking up DVI contexts inherit from DVI contexts. Then we have our constructor. And remember that a shortcut for constructors, see tar tub, tub epic fails because see TOR topped up there though. So you can always do that and generate your constructor. And inside of that constructor we have the parameters DB, context options room bulking up Didi contexts and options. And then we have to pass those down to the base. All right? Doesn't need any content. Just make sure you have that constructor. For our tables or RDB sets. What we have would be DB set of type room for the rooms, DB sets of type room booking for the room bookings. And then we have our protected over at void on modal creating ModelBuilder, right? So for this method where religious overriding a shortcut to get to end up with a little bit to just dive override and then space. And then it gives you all of the methods you could over it's evidenced serotyping model, then you'll see it pop up on model creating. So that's a good that method's know what we're doing inside of that method. Stub. This all undo all of that. What we're doing in that method stub is seeding some data, right? So I'm just seeing ModelBuilder dot entity room has data and then we're just adding three new rooms. Then you'll see that I have ID and name. So prior to this name did not exist. All we had in the room was ID, so you can just hop over to room and add that name property. No problem. After adding that name property, then you can go ahead and give it a name. You could say name, you could say description of whatever it is. We just want some sample data and you know, to make it look a little bit more sensible, add a little bit more like our application is coming together. No, in terms of foreign keys, we definitely need to add a foreign key reference, which in the room and the room looking inside of the room will king could OS. So I'm just going to rearrange this quickly. I'm going to say room ID, but I'm also going to add the navigation property of type room and call it room. And this would serve to let it know that this is a foreign key periods. So Entity Framework Core is intelligent enough to know that this is a foreign key. If you want to know more in depth things, a bolt hole Entity Framework Core works and how your model database is subtle foreign keys and so on. You can check out my course, Entity Framework Core, a full tour. So I won't get too in depth with the configurations and all of the possibilities of Entity Framework here. Instead, we'll just continous sitting on our data access layers so that we can get started with our projects, right? So at this point, I want to put in the repository. So that repository, of course, or maybe I should call this services actually data services, right? And then we could rename this to data services if you want. I mean, there are so many ways he can rename. Sometimes you name a name, a name, and then you run into problems. That is what I think I'm going to call it repositories. Because really and truly, even though we'll call it data services, they're really just repositories for functions. And he has, I'm going to add a new a new file. And I'm just barring the name there. So I'm going to add a new class. And I wanted to call it room booking service. Just with all the I, right. Let's add that. And I went to meet this public class and it is going to inherit from i room booking service. So that means I need a project reference to the core. And I'll do that and the using statement. And then the next important thing is that it needs to implement the interface. All right, so now we have our implementation methods. Of course we have the method still bought, not the actual implementation, right? So once again, if you wanted to see how that inheritance margin can help you, you'll see the blue markers up hearing to my left. And if I click, I can jump over to the main file. So that's a quick way to navigate between files and their appearance as it were being what they're inheriting from, right? So that is really it for null for, for setting up the data access layers. Well, once again, if you need to review those, pause and go through, see exactly what I'm mood where and, you know, you might have slight variations of some of the names that you used for your project versus your folders. And that's fine, right? As long as the general idea of why always split this out is there. And it is relative to change. It is philosophical at best. But as long as you understand where we are, what needs to go and the whole its variance and get the references rather than it should be fine. So I'll see you next time when we read our first integration test. 18. Unit Test: Retrieve Available Rooms: Welcome back guys. We are a bold to read or first integration tests or unit test against functionality expected from the Entity Framework. So we're already setup or DB context and we would have setup or other projects in the test project, I have created a file here, room booking service tests. So you can go ahead and add that class file. Of course, make it public. And then inside of this file we're going to have our first method. So it's public void, should return available rooms. So of course we have to decorate it with our attributes and at any using statement for that to happen. Now we can start our arrangements. So if we jump back over to our code for our processor, would remember that for us to get the available rooms, we needed to pass in the request booking date, right? So for us to test against the database, we have to simulate what the database would need to know if something's available. So first beat off the range would be to say var is equal to knew the time. We can just put in some test lead time, right? So I'm going to put in today's date which is 2021, 0 six comma 09. All right, and then I'm going to save our options. So the thing is no doubt we cause we need an instance of our database. We're going to have to initialize the options that we can pass in to the database context. So remember that's our context has the constructor which is looking for some options, parameter of type db context options and relative to the room looking up EV contexts, right? So I'm just saying var options are, you can see DB options or contexts options over you need to name this. So I'm going to say var DB options is equal to a new instance of new DB context options builder relative to room booking up DVI contexts. And we have the parentheses and we add dot, use in-memory database. Now, along the way are going to have to add a few references while, and you're going to have to add up project reference to the persistence, right? So once again you right-click dependencies. Go ahead and add persistence. And another one that you're going to have to add is one to Entity Framework Core in memory, which you can get through NuGet. So you just right-click lots of money and you get packages. You can actually just search for it in memory. And you should find it quite easily. And by installed it and I, you get all of the other and to the Framework Core libraries that are required. So you can just go ahead and add those two references. And then you should get rid of all the red lines that meet up here. Of course, you've got to make sure that you have the appropriate using statement as you go along. So here we're seeing use in-memory database, and this is the database that I'll be using. One more would be dot options. All right, so those are our DB options that we're building for the in-memory database, which will only exist for as long as it's needed to carry out the test. Alright, so that's fine. No, within the context of the range, just the same. We want a sample contexts. So I wanted to see VAR contexts using var context. So this is basically saying creates us scope where you have a context being equal to a new instance of our room booking. Baby contexts, which remember takes an options parameter, I give it the options that we just created, which is DB options. All right? And then inside of this using block, we're going to seed it with some temporary data just for the lifetime of this test. So I wanted to say contexts dot add. And you can actually just add a new entity of anything that is it recognizes, right? So in older versions of Entity Framework Core, you probably ought to be more specific contexts. The table which will be rooms, Dots odd. But then in the newer versions you can just say contexts dot-dot-dot, and then you give it an object that it knows, it knows. So you room. All right. Go ahead and add the reference. So I'm adding a new room and I can give it an id equals 1. Maybe if you want, you can give it a name. And it depends on how granular or how much detail you want in the test. So I'm just going to say room 1 and then semicolon. So I can add maybe three of these, right? Shin of them accordingly. So those are the three samples that I'm adding. And then I'm going to add to booking records for our rooms. So on for it to Dan 14 the day before. Alright, so contexts dot-dot-dot, new room booking ID equals one. So that room was booked on the dates that we have prepared. All right. And then the next room was booked the day before or the day after, whatever value you want to do. But for today's, only one should be. That's the scenario I'm creating. Of course, it's up to you. What do you want to do in your test scenario, right? At the end of all of that, we're just going to do a context dot save changes to make sure that the database gets the data committed to it. And you'll notice that I got rid of the curly braces, right? So they editor suggested that we just use Control dots and did the suggestion and change this into a using statement for the context. And then we can go ahead and write all our code. So that really sums it up for the range of the DB context lead also have to arrange an object of the service or a repository file to be called witch's room booking service. So the room booking service needs to be able to talk to the database to be able to carry out its operation. So that's why in room booking service, we went ahead and injected this context. If we're not entirely sure how to do that, for us to get the constructor CTO, our tub, tub. And then we put in the incidence of room looking up DVI contexts, contexts, and then Control dots. Assign the field and create. There we go. And then use the underscore, just the key called consistent. And that's all there is the injective. So no, even though the methods are not yet implemented, we do have a way to talk to the database through the context. So in our arranged and all, we have to initialize an instance of this service. Let me qualify that room booking service. Right? And room booking service is equal to a new instance of the class off the same name, room booking service, which takes the parameter off the contexts, which in this case is our in-memory DVI contexts. So now we're finished arranging, it's time for us to. So for our first act, we will have var, available rooms being equal to our room, booking service, dot, get available rooms. And we'll just pass in the data that we prepared. So on all available rooms for that. Simply an offered no, it's then for the assertions, whatever we went to assert. So at this point, if you want to use your fluid, assert your fluent assertions are should lay our default assertions, That's no problem. I mean, you'll see in how they all work. So, um, or at least certainly an assert, the default assert works. So at this point, you can feel free to use the one that you're more comfortable with. What I'm going to use the default assert this time. So I went to assert that the cone of the room, of the available rooms for this, it should be X number. How much money do you think it should be? So based on the scenario you presented, your probably have a different assertion from me. But I think it should be two, right? Because, and then this is going to suggest that a swap the arguments, right? So to equal to that. So I'm asserting that it should be at least two because I have rooms one through three and room ID with one is booked for it to be room ID 2 is booked for the day before, and room ID3 does not have any booking according to the database. So at least two rooms should be available. While the assertions that we can do would be to make sure that the rooms that we expect to be available are contained in the list, right. So you could assert the canteens. What's our available rooms? And then contains no would say give me the the datatype, what is expected and then tell me what do you expect to see in it? What these overloads are only showing four strings, right? So here's, here's a better one. So the third overload actually shows for an innumerable collection. And then it takes a predicate R, one of those Lambda expressions as the filter for the comparisons. So in other words, I would say lambda expression Q, which represents any one of the available rooms. I could say the ID should be too, or at least available rooms should contain something with the ID two. Right? I could do the same thing for the three. And I could also assert does not contain the ensured that it does not contain ID with one right here. Number of things you can do at this point. If you want, it does, sorry, it's I guess a name you could have asserted against the name. So it's up to you, but that does artist right now to make sure that we get available rooms. And once again, we're involving pseudo database glucose are creating the database, arranging it, sitting on top with some sample stuff. And then we're acting on asserting. So let us run this test. And we know by know that we're going to get read. But let's at least run it and make sure that our syntax is correct. And when we jump over to artistic explorer, we see all of the previous green ticks, but then we see that we have a new test project and is really known to our new test, which is giving us our system not implemented exceptions. So that's fine. So at least we know it's actually calling our contexts. Sorry, it's calling our service, our repository. And everything is okay on that front. So let's assume, once again, walk through how we arranged and acted and asserted for this particular test. Because though it's a different kind of test, this is an integration test where we're involving a setup that has those mocking our staging. Let's use orange staging where no staging system, that is a third party system because this, while the context exists in our database, EF Core is a third party library. So we're actually staging what that third, third party library would do on a normal circumstance with our regular database actually being connected to. We don't have that yet, so we can fake it by using the in-memory one, giving it that the name. So we can say, okay, use the in-memory database, called it that for now and build those options and in the context given to us by e, of course, so versatile that once we build options, we have different extensions. We can use SQL options, you can use PostgresSQL options. In this case, I'm using in memory. And some people would use SQL light options, right? So the fact is you build the options and the creator contexts. Relative to those options, you steal the data that you need in the database for this context, for this test. And then eukaryotes, protists as though you're testing against real data that should exist. So it is like a little step up from just creating a list, right? So that is our red tests for this activity. When we come back, we will implement our core. 19. Implement: Retrieve Available Rooms: All right, guys. So we just wrote or read tests for our available rooms, called the available rooms. And I wrote the code already and I did it just so we can go through together. But that's why you see that I have a green tick already. But by array, I have undone the changes. So I'm going to run again and make it red test. And there we go. So we're on parity null. So let us look at how we can implement this quote. So we're calling the method get available rooms. When I Control and F 12 is going to jump over and you're going to see the not implemented exception, which is what this is complaining about, and that's fine. So our expectation is that when we call this method, give it a date, it's going to return rules that are currently available given the data that is being requested. So what do we need to return here? We need to make sure that we write a query that is going to be reflective of the data that is expected. So I can say var, available rooms is equal to our context, dots rooms. So I can look in the rooms table and then obviously need a filter where lambda expression, how would I get the room? So I have two options. I could probably look in the I could look in the room bookings. Australia, the room booking stable, where the DNA is equivalent to the current digit. Right? But then how do I know if I'm lucky in the room booking stable? How do I know which tables are not? Right? So I guess I could select the room itself. So Q dot, dot. Yeah, we do have the room, so no, i'm know which rooms are booked. So I should I should change this to on available disks. So I'm actually doing this. This is like my usual thought process when writing older query, what are the, what is the easiest we derive the lambda expression to get the data wants right? So I can easily find the unavailable rooms, but no, I need to find the available ones. So I can say var available rooms is equal to the context dot rooms, dots where the Q lambda expression, Q dot ID of the room are rather, I would say where on available rooms. So they made this list. All right. So on available rooms is no list. Second, see where the list of available rooms dot contains the current room ID is equivalent to false. I'm actually doing this on purpose. I'm showing you the more complicated where you probably already figured out so you could write it easily and if you have them kudos to you, no problem. I'm just showing you what the thought process could be. Some Seeing get me the rooms where the unavailable rooms contains that. So I actually wrote equals false because of readability, but it could also be not continuous, right? But for readability purposes that interact or the equivalent to false. All right, and then we get that as a list. So if I do that and then return the available rooms, then theoretically I would have gotten what's unavailable, then phoned all the tough all the rooms, what is available by seeing ones. It's not contained in the list of unavailable, then please return it and we're returning it. So I'm going to run this test and see if this bit of code works. And when we look at our test, we see that we're getting green lights. It does work, right? So as we've been doing, we probably write the code one way we get the test to pass. And then we say, how could the I refactor this code to make it a little better or whatever, for whatever reason. So to me, this is two database because this is one to get what's not available, then want to check if the table has any off the watts is not available. And then returning the balance, right? All of this is really because we could have set certain navigation properties inside of the rooms, are inside of our classes to meet this more simpler. So room bookings already has an obligation. Property four rooms. So that's how we got that foreign key are, that's what we're going to use the infer that there is a foreign key relationship between the room and the room. And that's fine. Now if we're looking at it's on the level of the type of relationship. It's really a one-to-many, meaning won room is going to have many bookings. So yes, we have the foreign key relationship there. But then in room I have the opportunity and I'm just going through this in case you're not that familiar with Entity Framework Core, we have the opportunity to actually tell the room that it can expect to have a collection. And I'm just going to use lists of room bookings. So any one room can have multiple bookings. All right. And when to call it bookings to represent that it's a collection. So once again, Entity Framework, we just infer that one room, many bookings and one booking to one rule. All right. That's all we have right now. That's fine. So inside of the query, I can rearrange the subnets. Because in my mind, I could say return or let me just say get the available rooms. I can say var available rooms is equal to the context dot rooms are. Let me just duplicate this line. All right, and kind of do it from scratch so we can see everything happening. I'm just showing you all the options. So it's not necessarily that one works better than the other. I mean, if you're going to call new cost of going to the database more than once as better than fine. At the end of the day it is passing the test. It's just that the code could be written a different way to do one database called so var available rooms in this new statement. Var available rooms, yes, is equal to contexts dot rooms we're, and then my lambda expression here would have Q dot through bookings. All right, so now I have the navigation property for the room booking. So automatically Entity Framework would just say, I am looking at for any one room, gets all of the bookings that are there that I can see any on this collection and amplitude on other lambda expressions, I can see where there any bookings that have today's date or the date being requested, so did be equal to the bit. All right, so that's a complete rewrite and this line alone or places, these two lines. So I could actually remove that and then just return available rooms. And then if you, if you want to write it in even fewer lines, you could just return from the get-go. No need for that variable. Alright, and I'm going to just run the test again because I just did a huge roof OK, die. Condensed three lines of code into one line of code. And then I'm going to allow my unit tests to tell me if my refactor makes sense and okay. All right. As I'm getting some feedback, assert failure, it's expecting to one, It's only seeing one. So looking back at this, this is the problem I should have said is equivalent to false, meaning I'm looking for available rooms, right? So get me the rooms where they have any word. There are bookings with the date. And then I'm seeing falls are there are not any bookings with the deed, so it's more readable to you. So get me rooms where there are not any bookings with the deeds being requested. So let's rerun that tissue. So once again, you see how quickly I got feedback from our refactor, got overzealous. Anna said, Oh, I can do this in one line. Did it in one line, fail the test? No. I reviewed my one line of code and I'm getting a green test. So once again, this is a process of test-driven development. You write your test, you write your code, you refactor, you retest, and ECL quickly. We're doing all of this sort of scrolling through it. The reason is taking so long is because I'm trying to explain as I go along. And probably my monologues are extending the amount of time spent doing this. But I just want to make sure that you're appreciating the value of how these tests are giving us real-time feedback as to the performance or the accuracy rather, off our code. 20. Test and Implement: Save a Room Booking: All right guys, welcome back. So we're stepping into and what we're going to do is do our integration tests to make sure that it will actually save the data to the database, right? So similar to what we did for, or contexts for the should return available rooms. Because the context needs to live within the lifetime of the operation. We're not going to do a global arrange like we would have done for the previous test, sorry, for the previous test project where we add the one arange anyway, set one instance for everything. So in this case, the context is kinda fleeting and it needs to exist within the context of the test. So we'll do the arranged according, Right? One thing that we can keep our repeat for sure would be RDB options. Though. Once again, you'd want to kinda keep it unique. So make it unique by giving it a different name, right? So I'm going to Nehemiah and should save tests. And after sitting on the DB options, I'm going to create a sample room booking required. So I'm just going to say I'm booking room with the ID one on this date. And of course there are other fields. A good fill in the email and the full name. Well, you wouldn't put in the ID because we're about to see if it's a wouldn't provide the ID, but you can put in all of those fields, but others keep it simple and put in the room ID and the date just for our assertions. Alright, so now that we have that, we can move on to our other. So similar to what we did up top here with our DVI contexts. I'm just going to borrow that line and then I'm going to initialize my room booking service. All right. And then having initialize it, I can say room booking service thought save. And I'm passing in our room booking that we're saving right. Now after we have pretended to save ROTC of the nor in memory, there are a number of things that we can start asserting. One. We can assert that only one record went into the database because the expectation is that even when we do our application, when we call save, only one record should read the goin. As at the time I'm putting in one booking. It should never have to record. So we could actually just retrieve the bookings by seeing var bookings is equal to or contexts, Dots, bookings or room bookings. Dot sort lists. So we're querying to get MC, all of the bookings that are currently in the database. Then we can say var king, this one, the one booking that is supposed to be in there. And I can assert single. All right. So I'm asserting that only one record should be coming back. So if you hover over a single, you'll see it verifies that a given collection contains only a single element of the given type. So I'll see a single coming from the bookings. All right, So assert singlet, but it's also returns that type. So it's saying one. It must make sure that there's only one in there and I'm going to return the one. So if it fails here, then you know that it saved more than once and you probably have a bug in your code where it's probably committing more than once at the time of the CVE. So then after we get the booking weekend or assertions, right, So assert dot equals and we can start comparing the stored record, which in this case is booking with the original record that we would have sent over. So I can see assert equal room booking.com and booking, which is what we just retrieved from that collection dot dot. Assert that these two are equal. Also assert the room ID is equal. All right? And like I said, you could have put in data fields for the email and the full name, and then you just do your assertions accordingly. Well, it pretty much that's what our test is doing at the heart of it for the safe. And then based on the different business rules that you might have. At this point though, the service should not necessarily have any business rules that should have been taken care off from the core, meaning it shouldn't get to the sea of just cycle puts in all the validations to make sure that we've got to get that fire unless it meets certain conditions. So all of those validations really should go in the core level or the business logic level. But right at this level we're just testing what the B2B should do. Say you're testing at that point is really just based on the scenario of the fact that you're carrying motor query. Once you carry out this query, you are getting results like what you would expect. It's not. Does the query do this under this circumstance are not? That should be done at the business logic level. So let us run our tests, which we know will be green, sorry, will be rid getting ahead of myself there. So it's building a project and then Test Explorer is going to show us read tests. There we go, because it's not yet implemented and that's fine. So I can just click, jump over to the tests. And all these methods are really needs to do is add the room booking to the context and then CVE, right? So I went to replace this with contexts or underscore contexts, dot add. And then I can just tell it had room booking. And then underscore context changes. And everybody should be obviously know if I run the test again and I'm just going to run. All right, so you see here it has five tests surrounding this method. All of these past, least in the past, they were good tests and this one field. So I'm just going to run all the tests associated with this sieve method. And let's see the old com. Alright, so you'll see that it kinda skipped over what it didn't need to rerun, select this one with that white and green. Didn't get run this time because it's wasn't that tests associated with the CVE. However, all five tests Arnold policy including the one that we just wrote. So that's really it for testing if Entity Framework is going to see the record. All right, remember that's what we're doing. We're doing an integration test hole. Does our application interrupt with the RM or the library that is in charge of talking to the database. So that is what we have unit tested on once again into different work makes it so easy for us to just spin up a database for artists scenario and then delete it afterwards. So these databases will only ever exist once that test is being run. So that's really it for our integration testing. When we come back, we'll just review everything to higher level and then move along. 21. Section Review: All right guys, so that's really it for this section. We have added another test project. So we see in our solution we have quite a few projects and some of them need to be tested. Some don't necessarily need to be tested. We didn't have this domain because it's really just storing all of our class files that will later be turned into the entities for the database. We had added a persistence which will hose the repository or service our Data Service, I call it repositories folder, but I'm calling the file services. So we could call it that. You could carry it out if you have a button naming convention, that's fine. But the concept is that the implementation of the eye room booking service. So i room booking service will have all of the data related methods stubs least. And then they will be implemented by the room booking service, which will definitely have the concrete code. So at least when we were unittest in the core, we were really only unit testing against the eye room booking service. Our assertions where I guess the fox that the method's would have gotten a call at least once or never based on the circumstance. So for instance, in the save room booking requests, who'd see here that we were verifying that the save method got called at least once, right? So when provided with the current data, based on the business logic that we would have put in the processor, that service method should be called at least once. Now, once again, the core is where we implemented and tested the business logic, the rules behind when it makes certain method calls on what circumstance it behaves a particular week, that's fine. However, the integration tests tunnel sits underneath the business logic where we're integrating or where we're testing the systems that we're integrating with this situation. We're integrating with our database through a library called Entity Framework Core. So we're testing to make sure that when we call the methods that are actually doing the interdependent work or work that into different work core is behaving the way that we would like it to behave. All right, so that's what an integration test is. And at this level you, this is where you would start testing like how you interact with a QR Rabbit, MQ, right? Or service boss. You would evaluate to interact with an email service, anything that is a third party. Now you are not the one right in the East statements on the code directly for you're just writing code to interact with it, then that is where you start doing your integration testing to test how do I interact with it. After the interaction, did I get a result that I expected? So with all of this done, let us go over to our Git and a check in. Of course, we're going to write a message that is indicative of the work that was done. And then I'll just go ahead and commit all and sink. Once that is done, we can pause here and then we move on to the next section. 22. Understanding Unit Testing .NET Core Applications: All right guys, welcome back. In this lesson, we're going to be setting up our web project and we'll understand why it is important to unit test the actual web projects. Now for this part of it, I wanted to be using a web API for no particular reason will settle the fact that it's kind of easier to see how we unit test. We don't have the overhead of a user interface or anything like that. With API, we can use unit test the endpoints quite easily. But that being said, the concepts of unit testing, a dotnet Core API would be very similar for Razor pages up or an MVC app. The only real differences would be the return types because dotnet Core is so versatile that the intricacies between the different models are very, very minute. All right, so that's, the consistency is very good. So let's us go ahead and create our API projects. I'm just going to add a new project. And we're going to choose Core web APIs. If you don't have it to the left, you can always search for Core Web API are just that API. But once you find the template, what we're going to be naming this one is room bulking up that API. If you want to use Ariza pages aren't MVC app, you would name it wi, something else, but are not NVC R-dot pages, whatever it is, you want to name it. But like I said, I'll demonstrate using the API for the lower overhead in general. So we can go ahead and hit Next. And we can leave everything as is. And does he creates in similar fashion to what we had done with the other tests projects. We're just going to go ahead and add a new project, which will be an x unit test project. It will be a room booking up a lot, api dot tests. And then we can use that and create also. At this point I'm just going to go ahead and bridgette dependencies. So firstly for the test went, well, let me start with the API. So for the API, it is definitely going to have a Bridget dependency on the persistence layer and on the core layer. So you can go ahead and add those. Now we're not willing to move much further than this for now. Instead, we're just going to look at the concepts of why we would unit test at this level. So unit testing is all about finding auto bits and pieces of your application will operate on the certain circumstances. So the more coverage you have, the better in this situation, we already have coverage for our Up core. So any other processor or any other business logic class or operations that we add, we can unit test at that level. The persistence tests are to our test, hold the database operations with us, and making sure that they do what they are supposed to know when you add one, he only one gets added, et cetera. Now at the application level, it's more about seeing how the application will respond on the certain circumstances. So with any web application, when you're given valid input, you expect AS or an action to be taken if you're given invalid input here expecting certain actions to be taken, certain things can be controlled from the code side, not necessarily the UI or the presentations from the code behind or the little logic running in the background. So something like coding against invalid logic, invalid input, sorry, so the user inputs the wrong values. You have validation that should see if it is invalid, then returned to pj are returned some form of result in the case of an API. And if it is valid, then greed direct to another page or return a certain kind of results. So those are the things that you would want to unit test for this whole application level. So if we look at the sample project for the API, we see that we got weather forecasting controller which has one endpoint returning some results of type weather forecast. Know I'm going to show you what exactly a unit test would look like for that API endpoint or for the controller in general. So using the unit test one class that we usually delete, we're just going to write that kind of tests. Firstly, we need our project dependency, it being added for the API project to the API tests projects. So go ahead and do that. And while we're there, just go ahead and jump into NuGet and we're going to have to add the mock tool, the lock library will also need to add surely. So you see in hello shrewdly works, you see in whole the assert negative two x unit projects work. You can make your own inferences as to which one you prefer. That's no problem. But I'm going to be using surely for this set of tests. So install, mock and install, and then jump over to our unit tests one default file I came with our project, I haven't deleted it. We can use this just as a sample to see how this weather forecasts controller would get unit tested. So in our unit test on file, Let's just write a tester that's a should. Return forecast results. All right? No, In my using statements I have using shouldn't be using x unit and using System.in. Granted, as we type, you will see them. You get the chance to put in what is missing. So you don't have to necessarily replicate that right now. But as we go along, you see what needs to happen. So firstly, we need to mock the logger that is present inside of the weather forecast controller because we have this logger being injected in. So in order to access the logger, we need an object of this. And by knowing all that, we just mock these things as we go along. All right, So I'm going to have to say var logger Mach is equal to a new Mock of lager relative to weather forecasts controller. Of course, any missing types you're going to need the using mock statement of top once you type that. And then I logger is probably going to need are using statement. And then of course, weather forecast controller will need that using statement also. So just go ahead and Control dots and include all the missing references. Next stop we need to initialize or controllers. So I'm just going to say var controller is equal to a new instance of the weather forecasts controller, which is taking logger mock object as its parameter, much like you would see here. We're instantiating a new instance of weather forecast controller and giving it that logger object. So now that we've done all of that, we need to meet the Call. It only has one method that we're testing our justice, that we get about a result. So we really just need to call the get. So to call the getter, I'm going to say var result is equal to controller dot gets. Then we can do our assertions on the gets so or on the results rather. So I can assert that may be result should be greater than one. You could probably also assert that the results should not be null. Granted these two kind of go hand in hand. If it's not null, if the count is greater than one, then it evidently isn't null. But it is possible for this not be null, but the cones is not rooted on one, right? So, you know, you put in all the assertions, we see that we put in all the assertions that we'll want to make sure that our method is doing what we expect it to do. So I am testing after the fact in this situation. So this is where that divide kind of comes up with unit testing because sometimes unit testing is, is introduced after the fact. And then at that point it's kind of unclear what exactly should I be asserting relative to code that's already written, right? Versus when you write the test first, and then you write the quote too much the test, then you know exactly like for like what needs to happen. Either way, let us try and run this test and see if we get good results. And after conducting that test, I see that we have agreeing tests. So we have a green test of course, because it tests exists already, are the code exists already rather. So if I did something like return null, let's say either refactor know, and for, you know, I inadvertently introduced this return null somewhere in this method that is going to completely skew what it should return, then our test should know be failing. And there we go. So now we're seeing that they were getting another exception. And that exception is more having to do with our assertion that with the code itself, Fred, it's getting that knowledge exception because I've returning null. And this is null. So you can't find the cones off null. You can find the number of nothing, right? So let me rearrange this assertion and try again, we should see a different kind of era, least not unknown exception error. And there we go. So nowhere getting a message saying that the result should not be null, bought was all right. So that is our assertion. Know that I'm seeing it should not be null. So it's stopping before it's even getting to this one to feel it's letting us know that assertion fields. So that is a basic example of how we unit test controllers or by r, by extension, the web project in the ASP.net Core. So when we come back, we will be implementing our own controller and unit testing or own controller with the more meaningful or contact contextually appropriate unit tests. 23. Setup Sqlite In-Memory Data Store: Hey guys, welcome back. Before we go any further with our testing of our web app, I want us to set up the Datastore and I'll give him that this is just a quick sample obligation and I'm not going to go as far as setting up a whole SQL Server database for just one or two tables, we're going to keep its infant. We're going to use an in-memory SQL lite connection. All right, so in the startup dot CSS file, let's jump over there. And underneath the swagger implementation, we can start by saying var connection string, I'm just going to call mine Khan. String is equal to data source equals colon, memory colon. All right, now if you want more information on that, you can always look at how to connect to SQL light in-memory databases in a dotnet Core application, there's documentation on it, but I won't get into too many details, right? Null. Instead I'll just continue with a connection string. So I'll say var con, con. Our connection is equal to new SQL I t0. So OCC SQL ITE connection. And then we pass in that connection string. So of course by now we know whenever we see those red underlines, we control that. And then you see here I need a package for Microsoft dot SQL I dot core. So I'll go ahead and find and install the latest version. And when that is done, we can continue by seeing conic Sean dots open. So that at that point we'll open up the connection to the in-memory SQL database. Then we have to let the services and all that we need to add a DVI contexts. And our DB context will be what the setup in the persistence layer, which is our room booking up DB context, right? So it is relative to that control dots or I don't need control dot because we have on that. I'd using statements on paste. So when I copied and pasted it, you see it automatically added the using statement for the persistence. So I encourage you to use that feature a lot. So then I can give it some options. Where I'm going to see your option is to use SQL. It, which I suspect it's going to need some form of library. But until then I'm going to pass in the connection object Control dots. And that is not helping me. But I just don't bullet to NuGet with this one. So I'm going to go to New get for the web project. And I believe I'm going to need Entity Framework Core with SQL IT. All right, So in SQL idea and got a bunch of other results, it's actually easy to type in Entity Framework Core, and then you'll see two different work core dot SQL ITE say can go ahead and download and install that package. And then going back to our startup file, if I do Control lot, you'll see the using statement options come up and then we're in the clear. So that is what RDB contexts for our web application is going to look like. Of course, in a real setting, you would have a real database connection which will probably live in the app settings.js on. And then you'd inject that in. If you want to know how that happens, you can trickle one of my other dotnet core courses where we actually use real databases. But just for this scenario where we're just looking at TDD within this context. I won't go that hired, I'll just settle with the in-memory implementation. So know that that is done. When we come back, we can start setting up our controller and actually get hidden to some unit testing. 24. Create Unit Test Scenarios: All right guys, so let us jump right into writing all the unit tests for the API controller. So we only have one control, as you can see, we're just doing a proof of concept with a very small up, but we're looking at unit tests and the different levels. So this API is going to be pretty simple. It's just going to have under the API that we need to be linked as they move into the center. And then we're going to allow the user to book a room, want to return an old key results with result object as an all when you get to a result object. And we want to add validation and reject bad requests. Over in our code, there are few things that we need to do. One, create this new controller for room booking. So I called it room booking control. If you're not so familiar with APIs, you can always took off my API courses. But if you need to know how to add this controller right here and no, you right-click the controllers folder gluteal controller, and then hit API and create that empty controller with the same name. So that will give you this basic templates. In the same breath. You can also go ahead and hit new, create a new room booking controller test file in our API to spread or not when to show you that just yet because I have some coordinate. Well, we will go through that together so they can create that file. In the meanwhile, what I'm going to do, however, is head over to our core projects and bring up our Data Service, or sorry, not the Data Service or processor. There we go. So just the same way that we had our eye room booking service and then that eye room booking services really just Nikon obstructs on, on top of our data access layer. And then we have the implementation here. We're actually need our processor to be injectable. So right now it's just the concrete, a class that you create one time in one place and move along what then you see it's all the way over here in the core and we need, it's all the way up here in the API. So what I'm going to do is click on the class steam press Control dot. And then you see you have a number of options. One-off which includes Extract Interface. So when you create the interface before then, the inheriting class needs to implement that interface. In this case, we created the class already, so no, we need the interface. I'll just hit Extract Interface. Yes, I wanted to know new file and you see it will automatically give that file that I spur the naming convention. And it will, you can select and deselect whichever method you want in there, you click Okay. And then now we have our, our interface that implements the booking requests processor. Now this needs to be accessed by the APA. So to get it accessible and injectable, we have to come over to our startup. And I'm going to just row, I can do this under the db context. I'll say services dot, and we can add scoped. I once again, if you're not familiar with dependency injection, chuckle one of my dotnet core courses, what I'm adding scoped and I'm going to add the eye room booking requests processor alongside its implementation so that it can be injected and used by any of the controllers in our API. So that's all it takes, right? So let us jump over to the test file. So like I said, I've written some amount of code, but I'm going to walk you through line-by-line. Feel free to pause, replicate. But I will be explaining everything to you. I think they're just a bit the things for us to do then for you to watch me type. So I just prepared it's and then I'll walk you through it regardless. So I have all of the assets or at least some of the assets as I knowable, it's kind of prepared at the top. So I have my constructor where I'm initializing a room booking service to be a new instance of the booking requests processor. I have an instance of my controller. And remember that you can actually just type over this land and then use control dot to retroactively generates this method we had been doing it, sorry, this field. We've done it a few times prior to this. So for the controller, you see here that's I'm initializing my room booking controller and it has a red line because I'm also giving it the room booking service. Thought object. And this probably let me call this room booking processor instead of service, right? Just to be explicit and clear with the names, room booking processor and refactor along the way. All right. So the room booking processor mock object is being passed in to the room looking controller. So from here I can do Control dots and let it generates that constructor jumbo there. And then I'm just going to rename these room booking request process or room booking processor. With the underscore and one without. So know our controller has the instance of the room booking requests processor being injected in and ready for you. That's fine. Otherwise I have an instance of our request and an instance off our results. Now one more thing I'm going to do, and we can do this together is to set up this Mach, right? So remember that this mock needs to know that when it is called, what it should do. All right, so we're doing the setup. We're going to set the x-dot. And there's really only one method book room. You'd need a mock library of course. And I'll be using should live for my assertions, strives to just in case I failed to mention those earlier, make sure they go to NuGet and you install these two packages. We've done it before. And if you're not so sure, you can always just go to the previous test project. You can literally just copy these two lines. Go to your noticed project and paste them in there. And then just do a build and it will automatically install those packages for you. So let us set up our book room. So our book room or requires some form of request object. So I'm going to give it our requests here. And then it needs to return some form of results. So I'm just going to say dots returns are actually a type that's on her own place that should be outside dots returns. There we go. And what it will return is some form of results. Alright, so I'm just leaving these as well. They're instantiated, but they don't have any useful data in them just yet. That's fine. But that is our setup for our room looking processor. Now if we go down a little, you'll see that I have multi-purpose test here. And this test is going to cover the tool scenarios from our requirement. The requirement is that we need to allow the user to book a room. I went, it is invalid. We reject the attempt. Alright? So I have a theory and have two bits of data, two rows of data to be fit in. So the first one is seeing that this is the expected number of method calls, which is one. When IS model valid is true and it should return the type of Old Key Object results. So the problem is that there is type expected axon result type. So with APIs, you can return, okay, can return 404 or bad request, et cetera. So we want to make sure that when everything is okay, we get at least one method call to the book room and we are able to dismiss that Kirk's on quickly. Sorry. So we get one method call to our book room. And when it is valid, and we get that, okay, objects results when it is not valid, we don't want any method calls and it should be a bad request. So those go in line with three parameters and the name of statistics should call booking method when valid. Alright, I think that's straightforward enough. Now for the arrangement, I'm going to force the model state of the controller to be whatever was Boston. Well, they're true or false, right? That's is model valid. So if it is not valid, meaning we pass in falls, then proceed to add manually model state error. Then we are going to, I went to see our weights, the controller. So notice I'm using a weight. We weren't using asynchronous methods prior to this. And that was kind of deliberate else kind of skirting around that. But no, lets us see how we can make asynchronous tests. No problem with that. So async task, that is the signature for the test method. And then here we're awaiting our control or calling this method called book room, getting a request object. So what I'll do here is use control dot and generate that method. Jump over there and I'm going to make it an AsyncTask that returns I action results. All right, just to make sure that it knows that it should be returning something that is of an HTTP response message type, generally speaking. All right, so with that done, we get rid of that error. We have that mini implementation. Then we can assert that the result should be of type, expected auction result type. Alright, so whatever type is then I'm actually Boston with either scenario. We are asserting that that should be our type. I'm also going to see that our room booking processor. So I did a bit of renaming since we started this Lesson. Room looking processor dot verify that book room with the request is called exactly that number of times, which is being passed in through our inline data. Null. You can just do a build. And then once that build up succeeded, you jump over to Test Explorer. And then you'll see your API tests. And I'm just going to run the API tests. And we should be seeing or it tests all are owned, or at least for what we just wrote. Good. So we have to read tests because both of them are seeing system not implemented. All right, So we can pause here. Once again, you can review and pause and replicate if you need to. But when we come back, we will start writing the implementation. So we have a few things that we need to cover, including WHO validation will be handled, generally speaking, and what the controller method will be doing based on the scenarios. 25. Implement Code For Tests: All right, so we have our unit tests written. It probably still have some room for improvement, but it gives us a general idea of what we need to implement to get the basic requirement done. So I'm going to start off with the validation because I need a way to ensure that validation is actually being done. The cool thing about dotnet Core is that we just need to go to our model that we know is incoming and setup our validation. So just to verify book room is expecting room booking requests. Room booking request actually has no fields. So how do I add validators to the fields? Well, remember it is inheriting from our base method, which we can add the validations to. And everything that is inheriting from this base, which would be the requests, the results, and the actual domain object would get those validation rules. So the first one, which is the full name of the person I'm going to add required. And that the string length should be more, shouldn't be more than 50. Thinkable, be a bit sharp, fall full names. So let me put it to it. Of course, based on your business rule is you add your validations accordingly. All right, the next one, I'm going to make the string length the same. So for the email address, I'm also going to make it required and then I can add that I wanted to be an e-mail address. All right. The next one, I'm going to add that the did, the datatype is the IT. And at this point you could even think about other validations that you could put it into C that must be in the future or copy in the past, those kinds of things. That point you would either create a custom attributes or you can let it inherit from the invalidates object. And that will have you implement this method here with the eye innumerable validation result called validate and giving it a validation contexts. So at this point I can probably do something like if b is less than the datetime Node.js. All right, So here's what I'm seeing. If the data that is provided by the user turns out to be less than today's date. Right? Then I'm going to yield a new validation results where I send them Mrs, AND it must be in the future. So you can tweet that. I'm saying must be a future date, so I can easily say where it is less than or equal to. So that means they can't book for it to be our deeds in the past. They must be in the future. And then the second parliament, that sort of validation results would be the member name or names. Member names because it's an area where you can provide one value, which is what I'm doing. So the error message and then the aria with members that are affected by this validation. So here I'm just seeing new array passing in Name off beat. So for this, it's just the key what field or the name of the field with the error. The problem with doing this is that if I change this, the booking date later on and forget to update this string, then there's going to be a mismatch. So you'll want to strongly typed by seeing name off and then the field name. So it evaluates that would be the string version off the field name. And then the additional benefits to that would be that if I do a global refactor or even if I refactor this name and forget to change it down here, I'm just going to get compiler errors and I won't be able to push that erroneous code to production. So these are the things as we go along. Yes, It's test-driven development. But in doing this, we see best practices that reduce the likelihood of errors as we go along in our code. So I just added all of that little validation to our base. So that means inherently every one of the room booking variations will know have that basic validation. All right, so what we see here to result the request and we know the domain object for room booking will also have that. Now in our controller, we need to put in some code that handles our scenarios. So the first thing I'm going to do is check if the model state is valid. So I can say if model state is valid and this would be informed by the data coming in by the room booking request. And while I'm here, I'm just going to let it know that this is an HTTP method post, right? So this is HTTP post method. So just by calling room booking slash with a post request on that pillow that looks like our booking requests. It would know that this is where it's supposed to, the goal. So if the model state is valid, then I want to proceed to call the room booking processor. And I'm going to call the book crew method in the room booking processes. So if we are valid, then go ahead and call that method. All right. If however, the result flood soil, remember that actually this is returning. Room looking results. If we look and see what that room will kill result is, you see here that we get but not flood and we get that ID. So I'm going to know that I'm thinking bolted more winter. I realized that oh, maybe there's another scenario that I could test for, but fine. That does get the room booking results. So var results is equal to whatever result this term, this returns. No, it wouldn't be prudent for me to tell them. Okay. Boat you're booking failed because remember that this result has that flood success or failure. So I'm going to say if the results dot flood, sorry if there's a flood is equivalent to, and I can get the enums right there. And I'm going to say, if it is equivalent to success, then I want to return all Keras funds or turn, okay, kept the dough with the results. So remember that that result would have been compiled by our book rooms. So this robot DO down the daisy chain to see. So BOC room is calling result here, right? And then we're seeing creates that based on the bookend requests. So all of those fields that have been transferred over, okay? And then if there are any available rooms, then we want to return with the ID and the success flag. That's what goes into results. Otherwise, results silicones about with all of the values, just with the failure flood. Alright? So that is what we can do in that situation. So we just return The OK response with the entire result payload with the room booking ID, the room ID, the whole shebang, everything that would've been compiled from there. So if I run all my tests, Let's see. Oh, I'm sorry, I'm getting a compile error because not all code paths return. So I'm going to return bad request here, right? Does, if it doesn't get us far as the old K, then I'm just going to see a return bad requests. So let me just thrown on my tests again. And then you'll see that I have one green and one red tests. So the test is seeing that it should be of bad request object results, and it was only a bad request results. All right, So that brings up an interesting question. What's the difference between bad request object results and a bad request results? Well, the just the same thing with the old case. So OLC object results is different from, okay, right? So you see old key result is there because there are no parameters. So okay, by itself means no parameters. However, once you put parameters in there, once you're passing an object with the response, then it becomes the old key object results. So bad request by itself is the battery result by requests results. But we could put data in there. So generally speaking, when you return a bad request, because off a field validation, you can just put the model state in there. And the whole JSON will know how to de-serialize it and presented to the user that these are the validation points based on the failure, right? Similarly, when the flag is not successful, I probably don't want to give it the model state because the model state law is valid. However, the booking failed, maybe there were no available rooms or something. It wasn't really a field validation for the battery results or bad request. And this is where you probably start getting peaky based on how you want your API design to work. Well, what I'm going to do, it's already passed the validation here. And let's say there were no available rooms. So this did not give us the success flood. So under here, it clearly if we didn't get the success flag they needed or eternal key, once I went to do is say models, the model error, right? And this is me just assuming that it's only got this far because it didn't get the subsist Flau'jae was valid but didn't get the success flood, Someone's got a model error myself. And I'm going to add it to the name off. And the field will be coming from the room, will care request dot date field. Since I just wanted to point, they'll tell important using this name of parameter is. Because once again, if we refactored, this would definitely be considered in the either list of errors are a list of risk factors don't. So it's less bulk tracking and trying to hone bone magic strings that are throwing off the application. So I'm going to add a model state are just did a D8. And I'm going to see that no rooms available for a given date. All right. That will be the only reason it feels right. This thought based on the business rules that we have so far. And then that would be added to them, all those data, we still return the bad request with said model state. Alright, so right there all in one swoop where either returning old QA or returning the butter request with the validation errors. And this one is kind of ad hoc based on the process, right? So let us run all tests again. And now you'll see that I have that green tick, two of two passing and that's excellent. So you see we just did a quick implementation to meet our test logic over in our well, to meet artists logic, correct, we did the implementation, but we implemented something that we're not necessarily testing for our accounting for with artists, which is that result flag. So I'm going to jump back over to the test and I'm going to add that to the test-driven section. So I'm going to add the enum here. If I can remember, it, will booking result flag or add someone to add that as a parameter or data-driven test, it's nullable. And I'm going to say bookkeeping result flag. So I'm going to have to add that the inland data should, when everything is valid, have that flag for success. And when things are not valid, it should also have that flowerpot for failure. So in my range, I'm going to add this if statement to see if the book King result flag has a value, meaning one was provided, yes, one was provided. And frankly, I don't even need to make it more liberal, Come to think of it. I'm just thinking about introducing it after the fact. But I mean, I'm the one in control of the data, so I'm going to introduce it and leave it alone. So I don't have any scenario where it would ever be no, anyway, not in this test. So let me backtrack. It is not a nullable parameter. And we are passing in one for success, one for failure. And then remember that we had our result field being initialized up here. And the settle see is that the book room method when given a request should return a result. So I'll, I'm seeing here is that whatever value is in this scenario or in the data being passed in for that scenario, set that to be the flag for the result object, which is going to be returned once that method is called anyway. So I'm just going to go ahead and run these tests once more just to make sure that I'm still going to be getting a passing grade, right. It's alone. And there we go. So I'm still getting passes. And those are all the tests. So I'm just going to run all tests in view and everything is passing. So that is a very basic way to test your API and by extension your web application. Because the only real difference once again, between me testing an API versus you testing Razor pages up or an MVC up would be the return types that you're testing for based on your own. Now generally speaking, when incomes the MVC, you're either going to be returning a view or redirecting to another. Redirect to another view, our radar to auction rather. When it's our Razor pages up, you're either returning page or you're redirecting to page and you use other return pj, which is seen as page results. If I'm not mistaken. So you can have redirect to page results. Usually do that when it was a successful operation. But when it is not a successful operation, you usually just return pH. There we go. So I just need that library to do return to test for if you're just sending backup eat results. All right, so just the same way that if it is valid, you want to redirect to pitch. And if it is invalid, you'd be returned into paid results on IUIE, you'd be showing the user the page alongside the model state errors, right? If it was an MVC up, it would be, I'll just type auction results. Right? Say What do you there'll be an axon results or it would be a redirect to action results. But it's pretty much the same thing. The scenarios that you're testing for probably differ based on the application that you're testing. But ultimately the principles are the same. So that's really going to stop this lesser. I know we didn't get too complicated, but we did enough. So you can appreciate whole low-code flows and holy unit test at the application level, testing what the controller is doing and what our assertions might end up looking like. So I encourage you create the endpoints, go ahead and do some more tests and see if you can think of other scenarios that you probably want to test for. I'll be happy to hear. And if anything, you shoot me some feedback and we can work on it accordingly. 26. Run and Test ASP.NET Core Application: All right guys off to all of this, sitting up and testing. It's time to hand it over to the application users to actually tested to alter it. So to get the API up and running, we need a few more configurations. One is the add to the service, the IOC container, the font that we need, the eye room booking service, which is actually talking to the database. So yes, we have the processor. The processor relies on the service and our application needs to know that we need to have our representation of the service so we can have representation of the processor. So again, let's go ahead and add that services dot i room booking service, room booking service. And of course use your control dot to include anything that is missing. Now we can jump over to our Package Manager console. And if it's not where you see it on my screen for yourself, you can go ahead and hit tools NuGet, package manager, and to get the console. So far this console, we want to change our target project to the persistence project. Why? Because that is where our DB context lives. So all of the libraries and so on for the commands are about to run live in that project, we just needed to point there. So I'm going to say add dash migration, and I'm going to say initial migration. So quick remedy to that error is to get the design package in the API project, right? So if I hit persistence, you'll notice that it has this entire XML block dedicated to the design packet. So I'm actually just going to copy this. And I think another modification that we need to make is this two different we're core library. I think when we create that persistence, we used SQL Server. So, and then when we got to the API, we decided, Okay, we're going to use SQL light. So if you want to continue using SQL Server, you're comfortable with that settled then fine. No problem does make sure that you change our own, the connection string and everything for the API. Otherwise, if you just want to follow me and make sure you have no problems, you can go ahead and change this. You can just change the verbiage from SQL Server to SQL IT. Alright, so me and that change but still copy this entire block for the design. And we're going to jump up to the API, make sure that this also says SQL ITE and then write up a wall of that. I'm just going to paste that design block, someone to do a build. And at that point the API project with no pick up that it has that design package. And then I can go back to my PCM and try this migration again. And after a successful build, you'll see that we know successfully have our first migration. So after we have the migration, we just need to say it database. And that is done. So let's take a look at all of what we have. We have rooms of room bookings. These are all the tables that we created and some data that we would have seen it in. So you see we have rooms. See that in. If you want, you can create an endpoint where the user can get the list of rooms. You can create another endpoint to get the list of available rooms. But the point of TDD is that you're going to have to write that test first and then you implement. All right, so I think I've given you enough ammunition that you can go and do those on your own. For now, let us test the API just to make sure that our code for which we have excellent coverage actually works. So because during API creation we had ticked, include the open API support, we get this Swagger, right? So if you're not familiar with soccer, it is a powerful tool useful both testing and documentation of APIs. If you're familiar with it, then this should be a no-brainer has to hold to use it. So here you see they give us an example of what is expected when we're about to do a post request for a room booking. If I click Try it out, and I'm not going to change our own the values. I'm just going to click Execute, then I get this nasty looking error. So I'm just going to read through it and it is seeing SQLite except North. So split, There's something I missed off with the configurations. I'm just going to jump back over to the code block in the start file, we have our connection, we have or AV context. And I think I need few more lines to make sure that the database is created. So I just love this method, ensure database created and I'm giving it the, the parameter for the SQLite connection. So there's controller.js and generate this method which can be private and static. Right now for this, what I'm going to do is. Builder object. So in all the builder object already. So this has been to be a new DB context options builder of type the DVI contexts that we're using. And then know that we have that I'm going to say builder, you are suppose to use SQL lite, right? So I'm just going to copy this. After all of that, I'm going to say using var or contexts is equal to a new instance of RDB contexts. So I'm sorry, I missed off these lines earlier. Then we're just going to pass in the builder dot options because I need to give it the options anytime we instantiate that. And then we'll say contexts, dot database, dot ensure created. All right. We're just adding it makes sure that the database is created. Yes, we scuffled at all that up, but then because of the nature of the database, we need to make sure it actually gets created at runtime. All right, so let's try that again. So jumping back into swagger and doing the tote and the execute and know who you see something looking a bit better. So what we're seeing here is the response. So I mean, there are opportunities, of course, the clean it. So because you see here the flood is 000 because the success flag was 0 and the failure flood was one. We could go and of course then change the enum values. Or we could change the result field for the flood to be a string and actually print the words, the success or failure accordingly. That's up to you. If I put in something that is invalid, like I put in a deeds that was last month and try and execute that. Then you see here I'm getting that 400 model state or where the deeds must be in the future. All right, so those are the kinds of things that we have implemented and we have tested so we know the behavior once we try the scenario in practicality. So those are the different levels of testing we went through our red-green cycle, may ensure that's our code was bulletproof to whatever extent we did. And then know when the client is testing, they would have gotten their test document to see. Here are your scenarios test and make sure that these are the outcomes. Any variation from that would result in them wanting to tell you to refactor. So if I put in some invalidates, this is seeing the 18th month of the year and I click Execute, that is going to lead to a crash. So yes, it's still a bad request and yes, I'm trying to get some objects here. You're seeing the JSON value could not be converted, but then this is probably not what a user needs to see or somebody consuming the API. So at this point and all your QA department would come back to you and say, hey, you know, under this circumstance this happen, please call them, didn't see it for it. So you go back, you write a test to make sure that a scenario like that doesn't exist or it behaves this way. Or you write the test according to how you want it to behave. And then you write the code to make sure it does that. So in a nutshell, that is test-driven development with C-Sharp. When we come back, we can do a quick recap of all that we have gone through and then conclude. 27. Course Review: All right guys, so you've made it to the final lecture in this course. I trust that you have developed your knowledge of test-driven development with botnet core. And even though we learned dotnet core here, the fundamentals of it can be applied to any other framework or any other language that you might end up using. You have PHP unit for PHP development, you have JUnit for Java development, even Angelo and react. All of those have their own unit testing frameworks and the principles are the same. Test-driven development means that you're going to write the unit test is going to fail. You write the code and then you get it to pass on. In doing that cycle, you see how you need to wonky, dear cold kind of separate. So what are the things I wanted to point out is that we employed separation of concerns at every level. He tried to break all the different parts of our application into their own projects. So the API was by itself. The core, the business logic was by itself. And then we had the data related operations by themselves. Generally speaking, this is just a template based on their context. You may end up with more or even fewer projects. But the point is that when you have them broken out into those beads sizes are units, it makes it much easier for you to test the different layers of your application to make sure that each layer is acting as it should. And then the unit test itself makes sure that that component or that function functionality in that component or in that layer is acting as it should. So we have the persistence with all the data operations, with the core, with the business logic, all the if statements and making the decisions. Do we admit this date or do we not, that kind of stuff. And then we add the API, which basically represents the application. It could have been a UI. The API because of the lower overhead needed with the user interface and so on. But the principles of testing and development to remain fairly the same. Along this journey also learned that in unit testing you have different kinds of unit tests, projects, at least with dotnet core, you have MSTest, which is vanilla, that's straight from Microsoft. Do you have x units and you have n units? Each one has its own pros and cons. In this discourse, we use x units. And we saw that we could write test-driven, sorry, data-driven tests where we create a theory and then we could just pass in values that represent the different scenarios. Whereas in other frameworks it might not be that easy with my tough to write one unit test for the shoot and one R1 for when it is valid and although 14 and it is invalid. And so for maybe 23 tests that you would have written in the other frameworks, you would have written just a one test but with different data being passed in for the scenarios. And to me that's also helps to arrange the code because then I have to be mindful and know that this one method or represents what the method I am testing should do. So under different circumstances, what would be the old column? So then in my mind, it helped me to make sure that I'm writing code that is inclusive of the different scenarios. And write as little code as possible, but just enough to make sure that my unit test fields are my scenario actually comes to fruition. So we did application level testing, we did persistence of the testing, and we did business logic testing, and all of them so far are green when all that, when we make our fuck don't we introduce something to our code. We always want to make sure that the tests are passing and then we can move forward. So with all that said and done, I'm just going to check in the changes. And that is my message. So I'll just go ahead and commit all and sink. And you can see the latest source code. Should you need it in the resources. So thank you for sticking around with me and I'll see you soon.