Effective Software Testing with . NET and XUnit | Caio Sousa | Skillshare
Search

Playback Speed


1.0x


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

Effective Software Testing with . NET and XUnit

teacher avatar Caio Sousa, 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

      3:22

    • 2.

      Fundamentals of Software Testing

      5:51

    • 3.

      Unit Testing

      3:55

    • 4.

      Integration Testing

      3:33

    • 5.

      End to end Tests

      4:30

    • 6.

      The Testing Pyramid

      3:18

    • 7.

      Case Study Presentation

      2:23

    • 8.

      Applying unit tests

      4:27

    • 9.

      Getting started with Xunit

      8:09

    • 10.

      Working with Test Explorer

      5:38

    • 11.

      Improving the redability of your tests

      9:04

    • 12.

      More on Assertions

      9:00

    • 13.

      Fluent Assertions

      9:19

    • 14.

      Code Coverage and Tips

      5:40

    • 15.

      Unit Test with xUnit Review

      1:50

    • 16.

      Social Security Numbers Requirements

      9:09

    • 17.

      Mocking data acess with MOQ

      9:09

    • 18.

      Unit Testing the Requirements Part 1

      5:47

    • 19.

      Unit Testing the Requirements Part 2

      8:40

    • 20.

      Unit Testing the Requirements Part 3

      4:05

    • 21.

      Code Coverage

      4:28

    • 22.

      Course Conclusion

      4:10

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

1

Student

--

Project

About This Class

Learn how to effectively create unit tests in C# applications. Explorer the different types of tests, such as integration tests and end-to-end tests. Understand why testing is important and what type of test to choose based on the scenario you are in. Within this course you will create tests with the xUnit library and understand the real value in software delivery when quality is considered.

We will analyze use cases analyze the input and outputs of a given use case, discover how unit tests can best prepare you to ask the right questions and provide value to a software development team through a proposal of use case validation through effective unit testing.

This course covers but is not limited to configuring xUnit in .NET within a test project layer. The creation and management of test suits using a test explorer. The writing of test classes and test methods. Validation of test results through assertions, mocking dependencies to external services, and more. After this course you will understand how to create a robust suite of tests that help ensure the quality and correctness of your .NET code.

I invite you to join me in this exciting opportunity to ramp up your skills with software testing in .NET.

Meet Your Teacher

Teacher Profile Image

Caio Sousa

Software Engineer

Teacher

Hello, I'm Caio.

See full profile

Level: Intermediate

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: Hey there. Welcome to the course on effective software testing with D Net in EX Unit. Well, to start things off, let me present myself. My name is Cayo Susa. I currently work as a software architect, and I've been in the software development area for over ten years now. Within this course, I hope that I can share my expertise. That you can learn about software testing and how to make software testing effective when it comes to unit testing. I will also give an overview of the fundamentals of software testing and try to help you understand why we should test. And what are some of the software testing types that exists? Well, first, I will start by talking about the software testing fundamentals. We will review and explore the testing pyramid. We will cover theory and unit testing, integration testing, system, and manual testing. You will be able to understand why and when to apply each of these testing strategies. We will also look at a case study that involves college scholarships. Based on this case study, we will use unit testing techniques to test the logic and validate some edge cases. You understand the importance of unit testing and how this can reduce risks when it comes to making changes to code and validation of existing business rules. We will explore the fundamentals of X unit and understand why to use this tool. There we will implement code where you will implement unit tests and go over the X unit library and functionalities for unit testing. As part of this course, we will also explore other libraries that support unit testing such as fluent assertions. We will look at common practices such as using the Visual Studio test explorer, formatting tests and patterns such as the a pattern and code coverage. We will then look at the requirements for testing. We will apply unit testing based on software requirements. We will mock data access to facilitate unit testing and review our tests with code coverage. I invite you to join me in this exciting learning experience that will help you get to that next level. My goal is that after this course, you will have learned software testing fundamentals. You will have confidence to create unit tests using common libraries. You will be able to effectively create unit tests in the.net applications. Finally, I hope this experience can give you technical growth and expertise in the software engineering field. 2. Fundamentals of Software Testing: Hey, there. Welcome to the lesson on fundamentals of software testing. So let's talk about why somebody would test a software. So a software failure may compromise an individual project or organization. Depending on the bug of the software, an error can compromise a whole operation. Imagine you're implementing a financial software, and due to a major bug, millions of dollars are lost in transactions. That can compromise your organization, the project you're working on, and also you as an individual. Software quality. As a software engineer, it is your responsibility to ensure quality of the software you produce. Implementing high quality software can also lead to growth. The third point is security. Having a secure software is essential to protect against malicious attacks and prevent data breaches. You can think about testing authentication, testing authorization, penetation testing, and a lot more testing. For costs, saving your organization time and money, testing is one of the most cost effective ways to ensure the quality and reliability of your software. Finally, there are many more other reasons that testing can help. You explicitly or implicitly see this throughout this course. So let's dig in a little bit deeper when it comes to cost of software testing. The cost of bugs that happens in production often outweighs the cost of prevention. Imagine a stock trading app down for 2 hours due to a bug in code that could have been prevented with proper unit testing. Calculate the effort that it takes to debug an error, make the code changes, then deploy plus the human resource cost. If no testing is considered, errors might be common in the development process. Where Devs push code to specific environment, QA would find defects. It would go back to the developer that would fix it, then the code goes to production. You might have heard that the test is lengthy process, but practice makes perfect. Developers who have testing as part of their development life cycle usually have better results. What are some of the software testing excuses? Sometimes the responsibility is pushed to the quality assurance team or QA. However, QA cannot test every change to code, as they are usually not aware of all the changes in code. My software has few bugs. Even well architected software with experienced engineers working on them should still run into bugs, especially with the growth of the team, size, lines of code and patterns, not part of the requirements. Software requirements should defend testing through input of non technical requirements. Too time consuming and expensive. Like previously mentioned, fixing production bugs tend to be more expensive than preventing them. Not necessary for delivery. Software testing could be part of delivery. Tactics that automate and run tests with code coverage analysis could be part of a delivery pipeline. Let's look at some principles. So can I test everything? Testing all scenarios in software is not feasible due to the number of possible combinations and scenarios that can appear as software evolves. One must prioritize what should be tested, and we will talk about this when we get to the section about the types of software testing. When should I stop testing? The goal is to figure out what types of tests make more sense for the software that you are developing and what part of the software should you prioritize testing? With that, you would know when to stop testing or when to test at all. Where are to test. Some features of software are more critical than other, like a banking transaction module might be higher priority than a notification section of a software. Flawless testing. There is no test perfect software. As **** Straw states, testing can help find the presence of bugs, not their absence. There is no bug free software. Finally, conditions. Different software may require different tests. A web app may require strategies that differ from a desktop app that differs from a mobile app. 3. Unit Testing: Hey there. Welcome to the lesson in unit testing. So unit tests are small, as the name implies unit. When the goal is to test a single feature of a software, unit testing is a great choice. Unit Test validates a unit or a part of a small section of the code. Unit tests are fast. They're small and quick. Usually, unit test covers a method within a class if you're working in object oriented programming. In most cases, they test a part of the method or a behavior of a single method. So you would have multiple unit tests testing a single method for multiple behaviors. Let's look at some of the advantages. As mentioned previously, they are fast. They should be easy to control because they are small. They should be easy to write because they are small. And they also serve as documentation. Let's look at some of these advantages. Sometimes they lack reality because they test such a small section of the code. Since they're testing units, it does not catch all bugs, since it's not testing methods communicating with each other. And that's one of the biggest disadvantages. Well, with unit test, we can measure code coverage. By definition, a coverage metric shows how much source code a test suite executes from none to 100%. It is common to hear that the higher the cold coverage, the higher the code quality. Even though cold coverage can provide valuable feedback, it does not provide accurate feedback on effective tests. However, it is important to understand code coverage and to apply cold coverage when necessary. So let's look at cold coverage. The code coverage is basically lines of execution or lines of code and execution divided by the total lines of code. The code or test coverage metrics shows the ratio of these two items. And this helps us understand how much of our code has been tested. When it comes to branch coverage, it's basically the branches traversed, divided by the total branches. Branch coverage shows how many of control structures such as switch and nif statements are traversed by at least one test in the suite. And then we have the core logic or issues with metrics. It does not guarantee testing of core logic. So you could have high code coverage and still not test your core logic of the application because or logic has more to do with how you write your test than having tests written. To resume, let's go over effective unit tests. They should be low cost with high value. They could be automated, and they focus on the essential part of the code. 4. Integration Testing: Hi there. Welcome to the lesson in integration testing. So we talked about unit testing, right? They are fast. They test the unit of application. However, you cannot be sure your system works if you rely only on unit tests. There might be a necessity to validate how different parts of the software integrates with each other and external systems, such as the database, the message bus, emailing systems, et cetera. Integration testing helps validate how components work in integration with external systems. When you think about integration testing, it's important to maintain a balance between unit and integration tests. Integration tests validates integrations with external systems. Therefore, integration tests tend to be more expensive to maintain than unit tests. These tests typically runs more code than unit tests because it runs through external dependencies. This improves protection against regressions. More than unit tests. Regression testing refers to a software testing technique that re runs non functional and functional tests to ensure that a software application works as intended after any code changes, updates, revisions, improvements, optimizations, et cetera, are done. The number of tests between integration and unit can drastically differ from application to application depending on the needs. So let's talk about when to apply integration testing. Integration testing validates how software integrates with external systems and dependencies. And when it comes to these dependencies, we analyze two types. The first is control dependencies, where you have full control, like data access methods to the database. The second are uncontrolled dependencies, where you don't have full control, like a service bus library. What if I can't access control dependencies? So let's say that for security reasons, you don't have access to a real database for your control dependencies. In that case, you should avoid writing integration tests because it provides low value. Tests need to provide value. Conclude. Integration test validates how components work integration with external systems. They cover the presentation layer such as controllers, and they best protect against regression. Even though we are covering this at a very high level, integration tests are very important. They are not the goal of this course, since we'll be focusing more unit tests. However, understanding the types of tests available is crucial when it comes to planning for testing. Yes. 5. End to end Tests: Hey there. Welcome to the lesson on intend tests. Into end tests consist of tests that cover application workflow that is usually done by a person or a program. This tests helps validate systems where all its components are taken into consideration, such as the database, service bus, user interface, and other integrations. The user interface, such as the platform or browser navigation, and back end processing are also covered on ten tests. Enten test covers functionality of the entire software application with real world end user simulation and replication of factual data. At this point, the code or database is irrelevant. The idea is to provide an input to the system and expect an output through the same process as the end user. Into end tests are realistic. Therefore, doing this type of test, you should use a platform in which the system is running on. You could use a web browser to verify a web application. You could use an iPhone to validate an IOS application or Windows to test a desktop application. The idea is that you're using whatever the end user will be using. Right? So if you think of a car, you have a motor and you can test your motor or parts of the motor, that would be your unit test. If you want to test how the motor works with other parts of the car, that would be your integration test. And then your ten test would actually be getting into the car and driving the vehicle. Let's look at some of the advantages. First, tests are more realistic. Methods are not tested in isolation. Instead, you simulate the testing from the desired platform with inputs and outputs. They are reliable for regression. The test could go through all layers of the application, starting with the front end. And they also give you a confidence boost. For quality assurance team, end twin tests can be used as a final step before releasing go to production with confidence that existing functionality did not break. Let's look at some of the disadvantages. First, performance. System tests are often slow compared to unit test. Imagine everything a system test or ten test has to do, including starting and running the entire system with all its components. The test also has to interact with real application and actions may take a few seconds or even minutes. Second, environment setup. Tests are also harder to write. Some of the components such as the database may require a complex setup before they can be used in a testing scenario. Think of connecting authentication and making sure the database has all the data required by test case. Additional code is required just to automate the test. The behavior variation. Sometimes the same test might have different behavior results depending on the integrations. Imagine a system test that exercise a web app. After the tester clicks a button, a HTTP request to the web app takes half a second longer than usual. Due to that small variation that we often don't control, the test can fail, right? The test is executed again, and the web app takes the usual time to respond, and the test passes. Many uncertainties in the system test can lead to an unexpected behavior. 6. The Testing Pyramid: Hey, there. Welcome to the lesson on the testing pyramid. When working with testing, it is important to first have a clear understanding of the different test levels, their cons and pros. Therefore, your team can invest on whatever makes more sense to the objective of the organization or projects in hand. The following pyramid contains all levels of testing. If you research testing Pyramid, you should find a couple of variations of them. First, we have unit testing at the bottom. Well, all major business rules can be tested. Second, we have integration testing where you tend to have more complexity due to the integrations with external services, and they tend to be written for less scenarios than unit tests. Finally, we have to end tests at the top. It covers functionality of the entire software application and tend to be more complex to write and maintain due to the possible changes in the application. As seen in the figure, the bottom of the pyramid is less realistic and less complex, tend to be easier to maintain. And as you go up the pyramid, complexity and maintainability rises. Unit tests are easy to write. They are fast. They combine with developers expertise and could be released along with production code. For integration test, the focus is to test the integrations, and that might not apply to a huge section of the code. There might be specific scenarios where it makes more sense for integration tests. Finally, tu en tests can be more complex to maintain, especially if they are automated. Many organizations have a specialized team just for this purpose such as Kiwi. The focus of this course will be on unit tests. So let's summarize. The objective of software testing is about finding the presence of bugs. Even though there are multiple types of tests, testing everything on most systems is not viable or possible. Finally, we have test levels. There are different levels of testing from small unit tests to test that integrates with external services and each provide their own unique value. So it is important for you as a software engineer, QA analyst, or somebody involved in a software project to understand each of these test types, their level, and when to apply. I invite you to join me in this journey to go deeper, learn new skills, or update existing skills. The goal is for you to understand, implement and apply unit testing to your software application. If you want to grow as a software engineer, quality assurance analyst, or learn about testing, this pathway is right for you. 7. Case Study Presentation: Hey, there. Welcome to the lesson on a study presentation. So let's talk about the Cesar Scholarships K study. Cesar College gives a scholarship to 50% of their applicants based on their high school grade point average. After the application deadline ends, the submissions are evaluated and half of the students are awarded. A list of the awarded students is sent to the responsible sector. Now let's think how we would implement this in code. First, we would create a method called calculate awarded applicants. The method would receive a list of students and return the eligible students for the scholarship. The input is the list of student names in their respective grade point average. The output is the list of the awarded students. Here is a code snippet of the method calculate awarded applicants. The first part of the code is using a.net library called Link to order by the applicants in the list by the grade point average. The second part of the code is keeping half of the elements on the list and returning the second half. Since the list of applicants is ordered from least to greatest, this implementation should return half of the students with the highest GPA. Now the developer runs his application and does one test case, and it works, as you can see in this figure. We have Kyle and Luena with the highest GPA, and the program returns the correct awarded students. At this point, as a developer, you are happy with the result and deploy your code to production. You are working on a small team and there is no Kiwi members, something that is very typical in some organizations. However, a couple of hours later, you get a call from the client saying that the application does not work as expected. On the next lesson, we will review what happened and how unit testing can help prevent these issues. 8. Applying unit tests: Here, welcome to the lesson on applied unit test to a given use case. So let's go over the calculate awarded applicants method. Here's the code nippet for the method. And let's go over this logic really quickly. The first part basically orders the students by the grade point average. The second part is kipping half the elements and returning the second half. Since the list is ordered, we return the second half of the students with the highest GPA. So here we have an example of a unit test written for that method. For now, don't worry about the naming convention or how this was created. The goal of this test is to validate that the applicant list is empty when no applicants are submitted. When we have a oh eight pattern that we will go over in the next module, we have the arrange, the act and assert, as you can see. For this test, we arrange an empty list. We call the method, calculate awarded applicants, and store that in the result variable. Last, we validate or assert if the result is empty, which is the expected result. So the test passed in RNR code without having to run the actual application. Here we have a second unit test. The goal of this test is to return half of the applicants based on the submitted applicants. We arrange the list of submitted applicants and store that in an actual variable. Then we create a list of the expected applicants and store that in the expected variable. The method is then called and the applicants are stored in the result variable. We then validate that the expected applicants are the same as the actual. The test passes and we now feel more confident that the code is working as expected. Here we have our third unit test that validates one applicant. After writing the third unit test, you start to question edge cases. If two candidates apply, should we return only one? We question the product owners, and they say that we should have a minimum of ten students awarded. After the requirements are updated, we run the tests again for ten applicants, expecting those ten applicants to be returned. Basically, we work our test to fail, but to follow a new business rule. We run the following test, it fails because it returns half of the applicants. We now implement a change in the code. Here we check the number of applicants. If the total applicants are less than 11, we return all applicants. Then we run the unit test again and it passes. However, we are still missing something. Can you think of what is missing? What if 12 people apply? The programmer returns six. I'll let you figure out how to solve that. The goal here is to show how Unitest can help you validate the business logic of your application and make changes to your code with more confidence. Many times I see that the error developers make are on small details that Unitest can help and should help avoid. When writing unit tests, it helps you think more systematically. If we start analyzing other cases, we can think of things like, what if all GPAs are equal? What if the GPA is less than one? With the unit testing strategy, you can make changes to your code without having to worry about breaking existing functionality. Once you have tests that cover those cases, it should be a confident booster for all devs and they bring more value to the team and to deliver high quality projects. The code for this lesson will be attached to the course. 9. Getting started with Xunit: Welcome to the lecture on Getting Started with XUnits. So Xnit is a free, open source, community focused unit testing tool for.net. Written by the original inventor of N Unit Version two, Xnit is the latest technology for unit testing in C sharp, FSAP, visobsic.net, and other.net languages. X Unit works with R Sharper Cold Rush, Test driven.net, Xamarin, and other.net type projects. It's part of the.net Foundation, and it is currently licensed under Apache two. So why should you use Xnit? Well, it's well used by the community and proven to be a great library. As you can see, there's more than 450 million downloads on Nugget to this date. It received constant updates and improvements on a monthly basis, sometimes more than one time a month. It's a well designed library based on a very robust library called In Unit, and its current version is 2.80. If you're watching this video, I'm pretty sure that this version is already updated, or the 2.80 is not the latest version. Well, from now on, most of the examples will be implemented on Live Code. I will implement the examples on this module using Visual Studio Professional 2022. However, if you're using Windows, I recommend the free version of Visual Studio. That is the community version, and it has all the functionalities that we'll be using or that I'll be using within Visual Studio 2022. You can download Visualstudio on visualstudio.miicrosoft.com. If you're on a Mac or Linux, you can download Visual Studio Code. Alright, so let's get started with some basic implementation. To get started, first, you're going to open Visual Studio and create a new blank project. I already have my blank project created here. Basically, it's just a template where we can insert new projects. If you want to create a new blank project, just click on File New Project and select Blank solution, and that's basically it. Once you have your Blank solution created, or you can use the existing one from the last lecture, and I put the download link as part of the lecture. So you can download the existing one or you can create one from scratch just as I'm creating here. So what you're going to do is you're going to right click the solution, add new project. We're going to go ahead and select Class Library. If you don't see it here, you can kind of search for it here. Class library. In our case, we're focused on the C sharp, make sure you see the C sharp here and click on next. This library is basically a new layer. This layer is going to hold the business logic for the tests that we're going to create. Let's go ahead and name this business dot Caesar. You can name it whatever you want. I do recommend the prefix business just for context. Next, we're going to go ahead and choose Det eight. It really doesn't matter the.net version here. If it's standard or seven or eight or even a nine or ten, whatever version you see, since we're just going to implement some basic functionality. So I'm going to go ahead and choose Det eight and create and this will create a new business layer, or a new class library that we represent as a business layer. So now let's go ahead and implement some logic. So let's rename this. So I'm going to right click Rename the class one to calculator. And I'm going to go ahead and add a add method. So I'm just going to paste in add method. So this is a ad. It returns A plus B, and it has the parameters A and B. So we have our business layer and we have our calculator class. Very simple implementation. I can right click here, click on Build just to make sure we don't have any errors. Build succeeded, and that's basically it. Now, let's go ahead and right click the solution again, click on Add New project. Let's go ahead and add a new test layer, right? So let's go ahead and select a X unit test project. So the same thing here if you there should be like a test yeah, here it is. There is a test section here, so I'm looking for C Sharp. I can even choose all platforms test. And as you can see, there's MS test, N unit, unit test or unit test project for the net framework. And at this moment, we are focused on X Unit. But I just want to show that, you know, there's a lot of pretty nice test projects here. So let's go ahead and focus our mission here on X units. So I'm going to choose the X unit template for C Sharp, I don't want to Det framework, and I'm on Det eight. Click on next. I'm going to name this Caesar dot business because we are testing the business layer. Actually, let's go ahead and name this business. I think I accidentally inverted, but that's fine, business dot Cesar dot test. I usually I will usually do something like Cesar dot business, but that's fine. The important thing here is that we understand that this test project is testing the business dot Cesar project. So as you can see, the prefix is business dot Cesar. And then the suffix is test. So this is a test project that is testing the business dot Caesar layer. So I like to keep it very explicit. When I click Create, we see two projects within our solution. We have the business dot Caesar, where we have our business rules and the business dot Cesar dot test, where we have our unit tests for the business rules. As you can see, if I click the project, you'll see the target framework. You'll see a couple of libraries that are within this project. So you can see the target framework that specifies the framework of the project. You can see implicit using notable the packages that it includes. So it includes the X units, which is basically the package that contains the testing framework. You have the X Unit runner dot Visual Studio and the miicrosoft.net dot test dot sDK. Basically, they are required for you to run your test project within Visual Studio. You also have other libraries like the covered collector, which allows you to collect code coverage if that's what you tend to do. Now let's go ahead and end the lesson, right? We created our project. We created our test project. We can right click the solution and build. Everything is building successfully. And then on the next lesson, we will write some unit test. And also this project, you can download this template from the download section of the course. 10. Working with Test Explorer: Hey there. Welcome to the lesson in working with the Test Explorer. On the last lecture, we created our test and we created our test project and we created our business dot Caesar project. Within our business dot Caesar, we have a basic calculator, and then within the test project, if you create a test project, it automatically creates a unit test class. Here we have a fact attribute which represents a test method and this test method is called test one. We have a test class and a test method. Now we're going to explore this test method. Let's go ahead and click on the test tab here and then click on the Test Explorer. The test explorer is the name of the window that lets you browse and run your tests or unit tests within Visual Studio, right? You have kind of some groups here, like this first group, it allows you to run your tests, so I can run all tests. I can run selected tests, so I can actually select the test that I want to run. In this case, there's only one, and I can run that single test. I can repeat the last run, and then I can also run my failed test. Second tab here or second section here, you can kind of filter your tests, right? So you have the total tests. You can filter. All of them, I can filter, the passing tests. So if I click here, since there is no passing test, since I haven't ran anything, it just doesn't show anything. I can filter the failing test and I can filter the tests that were not ran. So if I click here, you can see that this test was not ran yet, so it's this plane here within Test Explorer. Then the last section here is a place where you can kind of configure your test. You have advanced options like changing the processor architecture and automatically running your tests after every test build, you have the options, some options here. So this is just to kind of configure how you want the tool to behave, and you can explore this if you like, some customization. Now, let's go ahead and write some tests, right? So first, I'm going to run this test, so let's go ahead and run it. Click on Run. And as you can see here, let me just click here so I can show all of the tests. You can see that it passed, right? So a test will fail when it explicitly fails. Even though there's nothing written to it, it does not explicitly fail, so it's passing. So let's go ahead and write some actual test, right? At this point, we're not worried about formatting our test. There's a lesson just for that. So I'm just going to copy some code here that represents a passing test that adds two numbers, right? So I'm going to call this passing test. And I'm going to use the assert. The assert is basically checking for equality. I'm checking if you explore this, you have an expected and an actual value. So I expect four, but my actual value is this because this is what I'm calculating within my calculator class, right? So for the calculator class to be used here, we have to reference our business dot test project. So let's go ahead and right click the dependency, add project reference, and click on business dot Caesar, so we can add our calculator reference to this project. There you go. You can now calculate two plus two and the expected value is four if we run the test. I passes. You can also debug, which is very useful. So if I right click the passing test, you can also right click the class here and either one of those and just click on Debug. You see it hit my breakpoint, I can kind of step into this as much as I want to. So here I have A plus B, it returns, and then it asserts, and it passes. So that's debugging unit test is a pretty useful functionality. Let me go ahead and paste some more code here. So I went and paste the failing test. So this is basically the same thing, right? We're adding two numbers, but we expect five. So let's go ahead and fail this on purpose. Let me run all tests. And as you can see here, there's a red X. So now my tests are failing because one test fails, kind of like all the name space fails, right? So here, if I click on the failing test, I can see kind of why it failed, right? So you can see that we're using the assert and the values differ. The expected was five, but the actual value was four. As you can see, we're trying to add two plus two, and it fails. So here you can kind of explore, right, or visualize how test explorer is very useful, right? You have the summary of why a failed test failed. You also have a summary of the passing test, like the duration, et cetera. So this is a very useful tool that we're going to be using throughout the course, and it's highly used by developers that works within Visual Studio. 11. Improving the redability of your tests: Hey there, welcome to the lesson on improving test readability. So let's analyze the class that was created when we created our first unit test project. When we look at the unit test one class, we will see the fact attribute. This attribute is used to mark a method as a test method. It signifies that a method represents a fact that should be always true. A test marked with the fact attribute represents a single test case. If the test method throws an exception or fails in assertion, the test is considered a failure or a failed test. Well, one of the most important things about testing is how to name your tests. The name of your tests when it comes to test methods should consist of three parts. The name of the method that is being tested, the scenario under which test or under which it's being tested, and the expected behavior when the scenario is involved. And why is naming your test so important? Naming standards are important because they explicitly express the intent of the test. Cest are more than just making sure your code works. They also provide documentation. Just by looking at the suite of unit test, you should be able to infer the behavior of your code without even looking at the code itself. Additionally, when a test fail, you can see exactly which scenario don't meet the expectations. We also have another attribute called theory. The theory attribute is used to define a parameterized test. It allows testing multiple inputs against the same test logic. You can provide one or more data structure via attributes like inline data to supply the test with different input values. Each set of input values is treated as a separate test case. If any of the test case fail, the entire theory is considered fail. The following image displays an example of a theory attribute in use. We will also implement this ourselves. A very common pattern is the AA pattern, arrange, act, and assert. This pattern has almost become a standard across the industry. It suggests that you should divide your test method into three sections, arranged, act, and assert. Each one of them only responsible for the part in which they are named after. Since readability is one of the most important aspects when writing test, separating each of these actions within the test clearly highlight the dependencies required to call your code, how your code is being called, and what you're trying to assert. While it might be possible to combine some steps and reduce the size of your test, the primary goal is to make the test as readable as possible. Now let's look at some examples in code. Cool. So here we have our unit set solution, and we have our Unit Test one with the failing and the passing test, right? So the first thing we're going to do is we're going to rename our class. So let's go ahead and rename the class to calculator. Test. The reason we renamed the class to calculator test is because the class that is being tested is called calculator, as you can see here. So when we have a calculator test class, this infers that calculator test is testing the calculator class within the business library class library. Go ahead and rename our tests. Just to recap, the name of the test should contain the name of the method being tested, the scenario under which is being tested, and the expected behavior. So I'm going to go ahead and rename this and call it add, which is the name of the method under line to separate the next part. And let's just say should add. Underline and the behavior of the scenario. So let's say when integer. Cool. When we read this, we can see that we're trying to add, I should add, and we know that the number is an integer. Cool. Now we can explore some more testing, right? So let's go ahead and copy this pastelss here. Let me rename this to a theory. And let me just put this on por role. Let's go ahead and add some inline data. Now that I have the inline data, I'm going to delete the failing test. I'm going to create my parameter. So in A, and B and the expected. Let's go ahead and change this to A, actually expected, and we're adding A and B. So let's say two plus or two plus two is four. Four plus four is eight and eight plus eight is 16. Let me go ahead and also separate this a little bit. Let's put what we expect here for the result. This calculator add and then we'll just replace this. Now the result would be my act. And this would be my assert. So we have A here, right? So we're missing the arrange. Let's go ahead and arrange the calculator class. I'm going to save our A new calculator instead of creating an instance. There you go. Arrange. We're arranging a new instance. We're acting on the results, and then we're asserting what is expected and based on the result, right? So let me go ahead and comment this guy. And let's go ahead and look at our test explorer. And run our tests. As you can see here, there are three tests and all of them are passing, right? We have one method that represents three tests. So just to review here, the arrange, it basically sets up testing objects and prepare the prerequisites for your test. The act performs the actual work of the test, and the assert verifies the result. So this clearly separates what is being tested from the setup and verification steps. Okay. 12. More on Assertions: Hey, guys. Welcome to the Lesson on Assertions. So here I've created a class called helper, and this class contains a G model, which returns a new instance of the helper model with the property's name, date of birth, bank balance, and favorite food collection. As you can see, our model is within the same file. Also have a method called process helper, which depending on the value, it returns an exception. The value is no, returns this. If the value is one, it returns a non implemented exception. If the value is two, it returns an exception. So with this class or this helper class is going to actually help us kind of go over the assertion methods. So within the business dot czar dot test, we have a helper dot test test class which we're going to go over. Let's go over or start with the Boolean assertion. The Boolean assertion is actually checks if the assert is true or false. So you have assert dot true and you also have assert dot false. What this is doing, this is actually instantiating a new helper model, and then we have the expected bank balance. The result is actually getting a model. And within this result, we have a bank balance. So we're checking if the bank balance is equal to 150, if the bank balance is equal to 150, then this will be true and this will pass. If I run this, you can see it passes. If I change the expected bank balance to 200 and I run it again, it should fail. And there you go it fails. Cool. So let's go ahead and move on to the string equality assertion. Now, the equality assertion is kind of it tests for equality with expected and actual value. And this is one of the most used methods for assertion. The Boolean assertion could actually be replaced by this one, and it's often ignored, right? Since the Boolean assertion kind of provides more information of why it fails. So here we have an expected name, an expected first name, and an expected last name. We get the result from the get model, and we're checking if the name of that model is equal to the first name if it starts with the expected first name and if it ends with the expected last name. I I run this, string equality, it's down here, it passes. So if I look at my get model, you can see that the name is Cayo Susa, and if I look at my expected name, it's Kyosusa, it starts with yo and ends with SSA. So equal starts with and ends with is some of our equality assertions. So let's go ahead and move on to the next one. So the next one is numeric assertion. So for numeric assertion, we have the assert.in range, and there's also the assert dot not in range, right? So here we're checking if the bank balance is 10-150, right? So if I run this, you can see that it passes because the bank balance is 150. So if we look at this one that fails, we're checking if it's 10-149. And if I click here, you can see that value is not in range. The range is ten to 149, and the actual value is 150. So on the test detail summary, it can really help us understand what happens. So let's go ahead and move on to the next one. So the next one is the reference assertion. So the reference assertion kind of asserts, you know, we have the assert dot same. There's also the assert dot not same. We have the assert dot no, assert dot N no. I'll assume that the same in the null is the mostly used one. So the same asserts that the expected and actual objects references are the same object, right? So here we have a result that is getting the get model, and we have the helper model, getting the result value. So these objects are actually result and result and helper model are the same object, right? So if I run this, you'll see a reference assertion past. However, this can get a little confusing, right? We have the reference assertion failure. So here we have the result. And then I created like a helper model here, which contains the same kind of the same type object, however, with the same values, but it's a new instance. So the helper model contains the same value name, same value, date of birth, same value, bank balance, and favorite food collection as results. So it's kind of like equal in that sense. However, it's not the same object since this is a new instance. If I try to check for if they are the same, it actually fails. Reference assertion fails. And then for our next assertion, we have the type assertion. For the type assertion, we're actually checking at times may want to assert that the object is exactly the type you expect, right? So here, the get model is actually of type helper model. So if I change this to helper and I run, we would expect a failure here. There you go. It fails, right? It's not the exact type. If we change it back or roll this back to helper model and run this should pass. And there you go. It passes because it's the same type. Result is of type helper model. Cool. So let's go ahead and move on to the collections. So collections are also highly used within a lot of applications. So this can be really helpful. So you can assert if a collection is empty. So our favorite food collection here where it actually has value. So if I comment this out and I run this, this should fail. Collection empty assertion, it fails because there's actually rice, beans, and eggs as part of the collection. It's not empty. If I uncommon this because this is kind of forcing it, right? It's forcing the favorite food collection to an empty collection. This now will actually pass. There we go. And then we also have the not empty. And if we run the same favorite food collection, we know there's rice in there, so it's not going to fail. There's the collection contains assertion. So this contains is actually checking that an expected item is found within that collection. So here, there's a couple of ways we can do this. I just use a fact, but you could use the theory or done it in a different way. However, let's just look at it how it is, right? So here we're checking if the favorite food collection contains rice, beans or eggs, and eggs, in this case, right? Rice and beans and eggs, and it does contain if I change this to egg, it should fail because it does not contain egg. You know, it contains eggs. Cool. And there's also, like, does not contain, so it does not contain any carrot. So here, it does not contain and it passes because it does not contain arag. For our last analysis, we're going to look at the exception assertion, right? And for the exception assertion, we're actually using the action delegate just to separate the arrange from the act from the assert. So here we have the act is actually the delegate is actually, it calls or sets the process helper, and then on the assertion, it actually acts. Here we're kind of expecting here an argument null exception. If we look at the process helper, when the value is null, it throws an argument Dull exception. So if I run this, Argument looks. There it is. So if I change, let's go ahead and change this to not implement not implement exception. If I change this to not implement exception and run it, it fails because it does not throw in not implemented exception. It throws an argument no exception. So this is also pretty useful and nice. 13. Fluent Assertions: Hey there. Welcome to the lesson on fluid assertions. So fluent assertions helps you write assertions in unit test that are both simple and expressive. The library contains a set of extension methods that can be used to specify the desired outcome of a unit test. It helps the assertion kind of look a lot more natural, and it can also be easier to comprehend. And it also keeps your code clean and simple. You can check out some more documentation on fluid assertions.com. As of 2024, there are more than 446 million downloads of the fluent assertion library inNuget. The version of this date is seven.00. This version can change depending on the time you're watching this video. So let's go ahead and look at how we can implement fluent assertion based on kind of our last um, video. So our last video, we talked about helper tests, right? We went over all of the or most of the assertions within the X Unit library. And in this video, we're going to basically update the library or include the fluent assertion to assert. So first thing you're going to do is right click package the packages on your business dot czar lat test, manageNugt packages. Here you can click on Browse and just type fluent. You'll see fluent assertions, and you can just download fluent assertions. As you can see, the current latest stable version is 70 dot zero. So here we've downloaded fluent assertions, and once you download it, you can just kind of force it or use it here. So using fluent assertions, and that's it. We can now use fluent assertions within our project. So I'm going to be doing some copy and pasting here just to speed up some of the typing. So here, instead of saying assertion dot true, we're going to go ahead and say result, which is the object that we're trying to assert. And then bank balance, which is that property, should be expected bank balance. So if you compare assert dot true result bank balance equals expected bank balance, result dot bank balance should be expected bank balance sounds a lot more fluent, at least it does, to me, right? It's a lot more readable and could you know, pretty easy to understand, right? So if I run this, you can see that it fails because I think we've changed this on the last lecture. So if I change this to 150, here you can see that it fails. It actually provides a lot of details here, see, 200, but not but found 150 difference of 50, so that's pretty nice. If we run this now, that I change it to 150, it passes. So let me just close that out. Cool. So let's look at our shring equality assertion, right? So here we have kind of like a three liner here, and you can actually make this. It's still a three liner, but it's a lot more fluent. So here we're checking for equality, starts with and ends with, right? So we're checking for the name. So result dot name should start with expected first name and end with expected last name and contain the expected name. So a lot more fluent, a lot more readable from my point of view. And if we run this, we should get a passing test for the string equality. There it is. String equality, passing. So let's go ahead and move on to the numeric assertion. So numeric assertion here we're actually checking for the range. So here, very similar to the previous test result bank balance should. So this should is kind of like it's going to be everywhere, and be in range of low and high range. So if I comment this out, we should see that this is going to work. See, there it is, it works. And then this one I can just copy and paste. I believe it's the same thing. This one is failing because it's not within the range. So let's see what happens. Numeric assertion failure, but found 150. Yep. So that's kind of like the numeric assertion failure. So let's go ahead and look at our next one, which is the reference assertion. So same thing here. The result should be same as the helper model. In this case, it should fail because this is a different instance of result. So this is failure. There it is. It fails. And I believe this should be the same. So this one actually passes because these are the same objects. So let's look at this. Result should be should be null, see, you can see, should not be null. So I could say, Hey, result should not be null and should be same as. So I can comment these two. Kind of run this, see what happens. There you go. Reference assertion passes. So let's go ahead and move on to our type assertion. So for the type assertion, basically, very similar to the previous ones. So results should be should be of type helper model. And this is the type there it is. And let's go ahead and look at the empty. Oh. Result favorite food collection should be empty. And then let's go ahead and look at the not empty. So should not be or search for empty should not be empty. There it is. Not be empty, should not be empty. So It's coming out these guys. And let's run this one. So this one's not empty. And then there's this one that is empty. There it is. They both pass. And then we have the collection contained. So this one is a little bit different because this one actually uses a list. So it does simplify it a lot. So instead of using variables, it just uses a list. So here we have this list within a collection within that the expected variable. We don't need a multiple contains. Now we can just use the contain. So here I can comment this out and I could just run this. I'm checking if the fare food collection contains the expected list. And as you can see, it passes. So let's go ahead and move on to the So this one's contained. The other one is just not contained. So basically, same thing here, results should be like a not contained. Not contained. And there is where is it? Collection does not contain assertion. Collection does not contain assertion, and for our last one is the Argument null. So for the argument no, it's basically Act should throw Argument null exception. Does. And there is there's our fluent assertion library implemented. 14. Code Coverage and Tips: Hey there. Welcome to the lesson on cold coverage and tips. So let's recap. What is code coverage? In software engineering, code coverage, also called test coverage is a percentage measure of the degree to which the source code of a program is executed when a particular test suite is ran. Many developers say that code coverage is not very useful when it comes to testing, what matters, right? So maybe if you want to test the business logic, et cetera. However, code coverage should not be used as a percentage goal. Code coverage tools should be used to enable developers understanding of what parts are not covered and why. So first, we have the line coverage, right? So developers who aim to achieve line coverage on at least one test case that covers the line under test. It does not matter if that line contains a complex if statement for, you know, full of conditions. If a test touches that line in any way, the developer can count that line as covered. We also have branch coverage, which takes into consideration the fact that, you know, the branching instructions like the ifs, the fours, the whiles and so on. So it makes the program, you know, those instructions makes the program behave in different ways depending how the instruction is evaluated. For sample, if A, if statement, you could have A and B, A or B, having at least a test case for the true and false condition is enough to consider that the branch is covered. And then we also have the Fine code coverage extension. So this extension supports.net core projects and.net framework projects. And it's an extension that you could use within the Visual Studio community edition. It's free, and it helps us understand and evaluate the code coverage for our application. So let's go ahead and look at the code from the previous lesson and see how we can use fine code coverage to measure the code coverage. So here we have the project or the unit testing solution. So the first thing you want to do is install fine code coverage. For that, you're going to click on extensions, manage extensions, and then you're going to browse for fine code coverage. There is fine code coverage. In this case, it's already installed, but you can just click on Install. I'll click this Color TweakerP here. You see there's an install button. So if you don't have it installed, there should be an install button, and it kind of installs it for you. Here we have a screenshot of how it looks, right? So let's go ahead and run this tool. So to run the tool, you're going to go ahead and click on View. Check for other windows, and then you should see the fine code coverage here. So to actually have this output here, we have to run our tests. So let's go ahead and run our tests. 13 passes to failed. The two that are failing is the one that we kind of expected it to fail. But let's kind of see what we're trying to look for here. So within our source code, we have the helper model. We have the helper, and we have the calculator class. And then you can see that there's 100% coverage of the calculator class. There's 70.5% coverage of the helper class and 100% line coverage of the helper model. We are missing tests within the helper. If we look at the helper class here, let me disclose some of this. We can see that it is red here, you know? See, these were not tested. So that's why there's 70%. And what is in green is what was tested. So here, there's, like, a yellow because this is a branch coverage, and then red is stating that it was not tested. So I can kind of see, okay, these are the lines that I need to test, right? These are the lines that are missing, and this is a branch that I might want to consider. It's a pretty nice tool. It helps you understand, you know, what has been covered, what hasn't been covered. You know, you can check the results. You can check for branch, check for line coverage, check the percentages, and it's a really awesome tool. So there's the coverage. There's like a summary, there's kind of some hotspots, if you want to look at some like cyclomatic complexity here and some coverage logs. And that's basically it. 15. Unit Test with xUnit Review: Hi there. Welcome to the lesson on unit testing with X Unit Review. So now that we've written our first unit test, we explored the X unit library. I want to incentivize you to write a software that simulates a calculator operation. Within the software, we want to make sure we have the basic operations such as add, divide and multiply. You should write unit tests that covers all functionality, passing and failing cases. After that, you want to make sure you run a code coverage tool so you can measure code coverage for your code base. Then I incentivize you to post your results on GitHub. So let's summarize this section. We started with getting started with X Unit. I explain why Xnit is such an important library. We created a testing project and explored Visual Studio. We wrote our first unit test. We viewed the unit test using Test Explorer and ran it through the Test Explorer. We formatted our tests using thea pattern, which is a highly used standard. We also analyzed cold coverage and some basic tips. Within this lesson, you should be able to comfortably write unit tests, run cold coverage, and have basic formatting for your tests. 16. Social Security Numbers Requirements: Hey there. Welcome to the lesson on Social Security numbers requirements. So let's talk about software requirements. Software requirements are a very valuable artifact when it comes to software testing. Requirements contains the feature or non functional constraints that the software must provide to fulfill the needs of users and other stakeholders. Requirements tell us precisely what the software needs to do and what it should not do. They describe the intricacies of the business rules that the software must implement and how they should be valid. Therefore, requirements should be the first artifact developers take into consideration for when it comes to testing. We will build a test suit using X Unit in Visual Studio based on the given requirements. We're going to create an application that inserts a user to a database. We're going to use the following data, the user first name, the user last name, the user date of birth, and the user's Social Security number. Users between the age of ten and 80 are allowed in the system, and the Social Security number must be valid. The part about the database, we're not going to implement the actual insert, but we will implement what would be a mock of an insertion of the database. And you'll see that once we start coding. For the Social Security number requirements, these are the requirements. First, the Social Security number should follow the following format. The first part is called the area number. The second part is called the group number, and the last part is called the serial number. The area number contains three digits and cannot be 000666 or 900-999. The group number may not be 00, and it's also a two digit number, and the serial number is a four digit number and may not be 0000. Cool. Let's look at some code. The first thing I did here is I created a Social Security Number folder on the business dot cesar class. For that, you can right click the project, click Add and then New folder, and then just name that folder whatever you want. In my case, I named it SSN, that stands for Social Security number. Now we're going to go ahead and click the folder, click on AD and add a new class. I'm going to name this class user. Right, so now I'm going to create a model for the user, so I'm just going to have it public and I'm going to create some properties. So first, I'm going to create the first name. Let's have the last name. Let's have the date t date of birth.Hom. And let's go ahead and have the Social Security number. So we have the first name String, last name String, date of birth, as datetime and Social Security number as string. Cool. So that is our user entity that we're going to be working with. Now I'm going to create a user data class, which is responsible for inserting the user onto the database. So let's go ahead and create a class called user data. I'm going to create make it public, make everything public. So let's go ahead and name or create a method called insert user. So public void Insert user. I was changing my keyword settings. All right, so public void insert user, and the parameter of this is actually the user. Cool. So we have a user as a parameter, and we don't really care about the database, so I'm just going to say throw out implement exception. We're simulating an insertion, but we don't really care about the database. Remember, that unit testing, we don't care about integrations. So a database is an integration to external system, right, that holds data, and we don't really care about it. However, we do need this method because we will be simulating an insertion to the database. So we're mocking that, right? So we'll talk about this on the next lessons, but let's just keep it like this for now. So we have the user data. And last but not least, let's create a Social Security number class. So I'm going to create a new class and name it Social Security number. I'm going to also have it as public. Cool. And I'm going to create a method called insert user. So public void insert user. If we were developing this and some type of structured architecture, we might have these classes at a different layer. But at this point, I would just keep it simple, right? So we have a method to insert the user. I'm also going to create a method of TableanPublic bowl, and I'm going to name it is valid SSN or Social Security number, which basically is validating the Social Security number. So social security number, right? I'm just going to have this for now just return false. And let's go ahead and missing a There you go. Let's go ahead and implement this method. So the or missing the user here. So the insert user uses the user model. So if the or if not is valid, user dot Social Security number, or if the Social Security number is not valid, we're going to go ahead and throw a new exception. Let's just say invalid data exception and I'm just going to try to get used to this keyboard. All right. So cool. We have invalid data exception, so I'm just going to say invalid SSN here. Right. Cool. Now we're going to instantiate a new user data, which is my data method responsible for inserting a user to the database just to follow our requirements. So if the Social Security number is valid, I want to insert. So I'm going to go ahead and say, Hey, insert my user to the database. So now I have kind of my skeleton of the application where I have my entity. Model. I have my data model, and I have my business logic, where I implement the validation and the insertion of the, you know, user. So within this or with this skeleton, we're going to implement, all of our unit tests. And, of course, we're going to actually implement the actual validations and Mc you'll see this on the next lessons as we implement and test our requirements. 17. Mocking data acess with MOQ: Hey, there, welcome to the lesson on Mocking data access with MC. So at this point, we don't really care about inserting the user to the actual database. We care about testing the Social Security number rules. Even if the implementation for the user data was ready and we had a database ready to go at the unit test level, we don't care about connecting to a database. Remember, this is not integration testing. So the solution here is actually to mock a user data. And to do that, we need to create a data or create a user data interface and then inject that interface within the code. And we're going to look at that within the code. Um, so we have the MQ package or mock package, MQ. It's a popular library for mocking in.net. It's currently on version 4.272, and there's more than 730 million downloads. To recap, a mock object is created to simulate the behavior of a real object in order to test the functionality of other software components that depend on it. So in our case, we want to kind of mock the insertion of the user to the database, right? Um here we have kind of what our user data contract is going to look like, so we're just going to basically create an interface called I user data, and then we're going to kind of use that interface and inject it within our code. So we're also going to use automok just to facilitate the dependency injection, the resolve of the dependency injection, and this goes in a little bit deeper, but it basically automates our mocking. So, currently Automoc has 15 million downloads. It's kind of a mocking container for M, and it really helps if you're invested in, like, inversion of control, and you want to decouple your unit test, you know, changes on the construction arguments. So let's go ahead and Look at some code here. So here on the user data, if you click on user data and click on control dot and extract the interface, this will basically create an interface called I user data. For us, it automates the interface creation, and then we can kind of use or inject the e user data within the Social Security number. So I'm going to go ahead and create a constructor. Here, I'm going to create a private variable. I'm going to inject this. There you go. User data, and then I'm going to use this user data as my contract here instead of instantiating a new class. So here and now I have a contract, you know, user data interface. And also, now my structure is coupled, in a sense, not coupled, but, you know, it has that dependency to it. So Automa kind of helps us resolve this dependency. It just magically solves that for us. So let's go ahead and close all of this, save it, close close and let's go ahead and look at our test. Here we're trying to test the Social Security number class. Let's right click the project, click on AD and add a Social Security number test to basically to the test project. Let me rename this to social security number test. We have the Social Security number test. So let me go ahead and copy some code here just for productivity purposes. So here we have Auto Marker. So I'm going to click on packages Add Nugget package. I'm going to search for Mc, and I'm going to go ahead and add the M or install the Mc library. Currently on version four dot 20. The kind of clear all these tests. I'm also going to add the Mu automoc. So install both of these libraries. These are the libraries that we're going to be using. Cool. So we have MQ, we have Mach Automoc. So if we go back to our test and if I kind of like control that here, you can see that kind of, Hey, use automoc. So here it's using Automoc now. And at the constructure of the Social Security number test, instantiating, you know, or keeping this object, kind of global right to the auto Marker. So I have to recreate this every time or for every method. So here I have my automrkers a global variable, and I'm going to go ahead and create a method called Insert user. So I have this method here. Called insert user should not throw an exception when user is inserted. So now I want to know, I want to make sure it doesn't throw an exception, right? So remember, when calling the user data, it actually throws an exception. If I go to the implementation, it's forcing the exception. We want to make sure this exception is not going to be thrown. So for that, we're going to first thing first, I'm going to create a private method called create user, and this is going to help us create users with a Social Security number. So this method returns a user. It has as a parameter of the Social Security number, it just returns an object with the first name, last name, date of birth, and the given Social Security number. So it's just kind of like a creator for us. So this is going to create that object for us, and I'm going to use this to arrange and set up our mocking. So let's look at our range here. Not this one, it's this one here. Let's look at this arrange here. This arrange, we have the user with valid social that we created with the create user method. This is just creating a user with a valid social security number where we then instantiate a instance of the Social Security number using our Auto MAC. Object. So this creates an instance, and it solves the dependency for us. And then we kind of set up our auto mock. So this our M, right? So every time a user data is called, we want to make sure this is not going to throw an exception, right? So we want to make sure that when this is called, we actually mock the insert user for this user with valid social. So this is going to prevent mocking this will prevent an exception being thrown because we're not actually going to call that method. We're going to mock the answer user method. So now we can act, right? So remember, we're testing we want to make sure we don't throw an exception, so we're going to use a delegate here. So for this, we're going to act or call the answer user as a delegate here, and this is going to help us test for exceptions, as we've seen previously on the exception. And for the assertion, basically, we just want to make sure that it does not throw an exception, right? So let's go ahead and run this test. And it fails. It's throwing a invalidated exception. Let's look at our insert user method and it's actually returning an invalid Social Security number. I'm going to force this to true so we can pass this validation. Of course, we're going to implement the code to validate the Social Security number. At this point, I'm just going to make sure my test passes. I'm going to force this to true for now, let's go ahead and re run this and should hit this breakpoint here. So it did hit it did call this method and hit all of this, and this is marked. This is bypassed and I successfully moked the user data dot insert user, which is basically what I was looking for within this lesson. We successfully mock this and on the next lesson, we're going to go ahead and implement some of our business logic. 18. Unit Testing the Requirements Part 1: Hi there. Welcome to the lesson on unit testing the requirements Part one. In this lesson, we're going to go over the requirements and implement the age validation. So we implemented the application that inserts a user to a database, and we also implemented the model that we insert to the database. So we have the first name, the last name, the date of birth, and the user Social Security number. In this lesson, we're going to go ahead and implement the validation of the age of this user. So users between the age of ten and 80 are allowed in the system. So let's go ahead and look at some code. So here we have our code base with our unit test and the create user. So first thing I'm going to do is I'm going to create a new test to actually try to insert a user that is younger than ten. So I'm going to go ahead and paste this test. Over here so we can go over it quickly. So here we have a method called insert user, and it should throw an exception when age is younger than ten. Remember, the age range has to be older than ten. So on the arranged section, I've implemented a variable that receives a date that is one day from being ten, right? So you're 10-years-old plus one, that means you're nine days and one day from being ten. Then we're going to create a user based on that date. Let me go ahead and base the code for creating the user based on the date. This code here creates a user based on the date of birth. Same thing as this one, this one creates based on the Social Security number, as you can see here, and this one creates a user based on the date of birth. Cool. So we created a user here with an invalid age. We created our Mc instance in our user data mooc. Let me go ahead and update this to insert user. And then basically, we're asserting that this should throw an invalid data exception. So let me run this test, and let's see what happens. Cool. As expected, the test failed because we haven't implemented the logic for the age validation. So let's go ahead and look at the code base, right? So let's go ahead and look at the Social Security number class. So this type of implementation is something that is used in TDD, right, test driven development, where you first write your test, they fail, and then you write the code to fix it. It's really helpful because the test is actually validating for what we're looking for, right? We're looking for an exception when the age is younger than ten. And we haven't implemented yet, so it kind of, like, it alerts us, Hey, you have to implement this before you can move on. So it's a pretty nice way of developing. So let's go ahead and let's implement the age validation. So I'm going to go ahead and paste some code, again, just for productivity purposes. And then we can go over the code. So this is an age validator. It's basically a bowl that's, you know, returning if the age is valid, if it's true or not. Here, there's a method called get age where we get the age. So this calculates the age based on the date of birth. And then if the age is less than equal to 80 or greater than or equal to ten, a return is true, right? So it has to be 10-80-years-old. And now we can use this is valid age within our code base, right? So I can use this. I can actually have a IF statement here. I is valid age, and we can get the date of birth. If it's not valid or if age is not valid, then we can throw invalid data exception. And let's just say invalid age. Cool. So now we have our age validation. So let's go ahead and run this test again. And there you go. The age validation passed, right? So we've implemented our logic, we've implemented our unit test, and we validated the age less than 10-years-old, right? And now I encourage you to write a unit test to validate the age other than 80, right? So we can meet the business rules. 19. Unit Testing the Requirements Part 2: Hey there. Welcome to the lesson on unit testing the requirements part two. So let's review the requirements. We first created an application that inserts a user to a database. We then inserted the following data, first name, last name, date of birth, and user Social Security number. We validated the age, which is less than ten, and then we're going to go ahead and move on to the Social Security number. So a Social Security number must be valid. For that, we have the following rules, right? The source security number has the format of a three digit number two digit number four digit number. The first part is called the area number. The second part is called the group number, and the last part is called the serial number. The area number cannot be 000-66-6999 or 900 or 999. The group number may not be 00 and the serial number may not be 0000. So let's go ahead and look at some code. So we're going to follow a TDD approach, and we're going to first write the unit test and then go ahead and implement our validation. So for productivity purposes, I'm going to go ahead and pay some code, and we're going to go over this code. So I created a theory that tests the area. I'm testing for area that starts with 000666 900 or 999. And basically, the name of the method or yeah, the name of the method is the name of the method under test and then what you know, should throw exception. So we're expecting an exception when the area is invalid. So here we have we create a Social Security number based on the inline data parameters. So all of these are invalid. We have the Mc for the Social Security number business class, and then we have the Mc for the eye user data. I'm going to go ahead and fix this insert user, and we call our action and we are expecting an exception. So if I run this unit test, Let me expand. It does not throw an exception. It actually throws an exception. Let's look at this. So it's actually throwing an exception. Let's look at our create user based on the date of birth or actually the Social Security number. So the date of birth is date time now. That means that the user is younger than ten. So let's go ahead and fix this. Let me say add years -11. Let's go ahead and run this again. And there you go. Now, it's not throwing an exception as expected because the area is not being validated within our business logic. So let's go ahead and look at the area here. So when we go to the is valid Social Security number, we can see that we don't have any implementation yet. So first thing we're going to do is implement the logic for the area. So let me go ahead and pay some code. So the first thing we did here is grab the first three digits of the Social Security number. So we're getting the first three digits, which represents the area. And if the area is 000666 900 or 999, we return false. And basically, this is validating the area, right? So now we have the area validation. Let's go back to our unit test and run this. And there you go. No exceptions were thrown. Actually, exceptions were thrown and the test passed, right? So we expect an exception to be thrown because if the source security number is invalid, it throws an invalidated exception. Cool. So let's go ahead and move on to our group. So let's create the unit test for our group. So for that, I'm going to go scroll down here to let me just close this. So scroll down here and create this method. So this one is called insert user should throw an exception when invalid group. So an invalid group cannot contain the digit 00. In this case, I'm going to update this, insert user, and Walla there we have it. So let me run this, and this is not so it does not throw an exception because we actually were not validating the group, so it failed, right? We're expecting an exception, but it did not throw an exception. So let's go ahead and go back to the social security number validation, and let's implement the logic for the group validation, which is very similar here. So we're going to get the substring for the group. So it's between, it starts on the index four, and it's length of two. So this kind of gets the substring for the group, and if it's 00, then it returns false. I can just click here and run my test. And there you go. An exception is thrown, so the test passes. And last but not least, let's go ahead and implement our unit test for the serial number validation. Very similar to the previous ones. So let me minimize this. Insert user should throw an exception when invalid serial. Let me correct this. Insert user. So here we're looking for serial number that is equal to 0000. If that is the case, it should throw an exception. So we would expect an exception. If I run this unit test, it should fail. It fails because we haven't implemented that yet. And if we go back to the implementation, we can go ahead and just implement that part. So here I'm going to create the serial section. We're going to get the last three digits. And if those last three digits equals to zero, zero, zero, then we'll return false, and that will throw an exception over here, right? So if I re run this test, there you have it. We've implemented our testing logic based on failure scenarios. Of course, you can explore passing and failing scenarios. I always like to start with the failing scenarios, but then here you can explore even more. With this type of unit testing with this type of implementation, it's a lot safer to make changes, right? I can easily make changes to the business logic. And run my NICS to make sure that, you know, all the validations based on my requirements are met. So, you know, this gives developers, you know, it gives software engineers a sense of security. You know, you can evolve your software. You can work with your software and make sure that, you know, basic functionality, basic requirements are being met. 20. Unit Testing the Requirements Part 3: Hey, guys. Welcome to the lesson on unit testing the requirements P three. So we've implemented our area group and zero validation. However, we're still missing a validation for the Social Security number. We haven't yet unit tested the format of the Social Security number. So let's go ahead and implement that. Let me go ahead and copy this one because we're going to use theory, and I'm going to paste it here. I'm going to say Insert user should throw an exception when invalid format. Cool. And let me go ahead and test a couple of invalid formats. So I'm going to say AAA one, two. So this is an invalid format. Let's see here. One, two, three, instead of a dash, I'm going to put a number, so not having the dashes. Let's think here. Let's have null null as valid format. Let's also have empty. And let's just run this to kind of see what happens, right? So we haven't implemented any format validation. Let's see if an exception is not thrown for any one of these, then, you know, we already have a couple of errors. So let's run this So an exception is not thrown for this one, and for the seems like the null, right? So two of our test cases failed. We're basically expecting an exception for all of these invalid formats. And there's probably a lot more invalid formats that, you know, if we stop to think or, you know, think about it, there's probably a lot more you can insert or you can have here. And this is pretty nice, right? You have one method that's validating like four invalid formats. So let's go ahead and implement our format logic. So let's go to the is valid Social Security number, and the first thing we're going to do is validate format. So I'm going to go ahead and paste this code here so we can go over it. And here we're going to use a library called Regex, which is a library that helps us validate formats based on regular expression, right? So here we're checking if there's a match where the Social Security number has a three digit number, and then there's a dash, and then I'm expecting a two digit number. And then another dash and then I'm expecting a four digit number. So basically, this is validating for the Social Security number. We need three numbers, two numbers, four numbers. If it's a letter or something else, if it's not a number, if there is no dash, then the validation should fail. So let me go ahead and run this test again. And there you go. All of these all of these all of this data here is invalid and our unit test validated that all of these inputs are thrown in an exception, right? So we're expecting an exception, as you can see here, and all of them are now thrown in exception. So we successfully validated the format. 21. Code Coverage: Hey, guys. Welcome to the lesson on code coverage. So let's go ahead and analyze our code coverage for Social Security requirements implementation. So first thing we're going to do is click on View other Windows and then search for fine code coverage. I'm going to go ahead and click on the Test tab, click on Test Explorer. I'm going to filter the Social Security number test classes, and I'm going to run all my tests for Social Security number. And now we have our fine code coverage for those classes. Let's analyze the Social Security number class here. Here we have 97.2% line coverage and 88.4% code coverage. If we open the class here, we might be able to see some results. Here we're covering all these lines, we're covering all these lines. For the age validation, we are missing the validation of age bigger than 80 or greater than or equal to 80, right? So remember, this is the part of the branch that is missing. And then also the get age. Even though it's a private method, we're still missing, you know, the get age, which, to be honest, might not be that important. However, remember, code coverage helps us analyze what we've missed, right? So let's say that you're implementing your business logic. You understand that you missed this, but you consider this not to be that important, right? Maybe what's more important is your actual business logic, which is your serial number, your group, your area validation, of course, your age validation is important. We want to make sure we cover all of that. So this could be some homework if you want to uh, you know, explore and implement and get 100% unit test. I think this would be a great opportunity for that and also validate the age that is greater than 80 is another something that you could do and hopefully get 100% if that's something that you would desire. So the real value here is, you know, like I've previously mentioned, is validating your business rules, right? We've implemented an application, a very simple application that has no access to the database, but we did simulate database operations and inserts. We have our business rules implemented for the Social Security number, and we were able to validate, most of our business rules. We validated most of our business rule. This gives, you know, software engineers more security. It gives us, you know, power to make changes or security to make changes, right? We can make changes and in a sense, guarantee that our business rules, our logic is being met when you refactor, when you update your code base, and you make changes. This is a very simple application, however, unit tests are small, right? So when you get to the real world, you're still writing small methods. You're still writing, you know, you're still validating your logic. Even though this is simple, this is very applicable to real world scenarios. I hope that this analyzing the code coverage, implementing unit test, reviewing the requirements, open your eyes to the value of unit test. The goal here is to, um, sure that you as a software engineer, understand the value of unit tests, how it can help you evolve as a developer, help you evolve your code base, right? Write better code, write smaller concise methods and validate those methods, validate your rules where you can have a software that could evolve, right to better architecture or to meet whatever business requirements or the goal of your product is. 22. Course Conclusion: Hey, there. Welcome to the course conclusion. First of all, if you're watching this video, I would like to congratulate you for reaching this far. I would say that 90% of professionals don't invest the time they could or sure in learning, and you are part of the 10%. Index in this section, we will go over the main topics taught in throughout the course. So let's overview. First, we went over the software testing fundamentals. We talked about why software testing is important, such as the cost of bugs, some excuses, testing excuses, and also principles of software testing. We went over the definition of unit testing where we talked about pros and cons and how do we effectively unit test. We also talked about integration testing, where we looked at a testing balance to compare unit testing to integration testing. And then we went over to ten tests, where we also looked at the pros and cons and when ten tests are most commonly used. Finally, we looked at a testing pyramid where we compare unit integration and into end testing, and we had an image where we checked reality and complexity based on the type of testing. Next, we looked at a use case. We checked out the Caesar scholarship. We analyzed the input and output analysis with its basic requirements. We then released, right? We did a production release thoughts of how this application would be released. And then we went over the first steps on unit testing, like testing the award calculations. Finally, we talked about the unit testing with X it library. We discussed why this library is so important and why we chose to use it. We then talked about the test project layer within Visual Studio. We worked with the test explorer, where we ran unit test based on present condition or preset conditions. We then worked on formatting or tests where we analyze the fact attributes, the theory attributes, the AA pattern, and some testing naming conventions. We then moved forward with assertions and fluent assertions, where we tested types such as Booleans, exceptions, numerics, and other types. We looked at fluent assertions for better readability. We then went over code coverage and its basic definitions. We then talked about requirements for software testing. The requirements were based on birth date and Social Security number. We created a user entity, a user data, Social Security number class implementation. Then we understood that we had to mock data, so we used the MQ library for that. We also use libraries such as Automc to facilitate mocking, and then we implemented mocking with our data access. We then implemented the unit tests for the age in the Social Security classes. And finally, we did an analysis of the code coverage based on the code that we've written. So that was a lot. I would like to thank you for watching this course. My hope that this help you understand the importance of testing and applications, and this will help you evolve as a software engineer. Remember, to keep learning and evolving until next time.