The course is part of this learning path
Test-Driven Development (TDD) is a discipline that helps to create error-free, quality code, and drives productivity by reducing and eliminating errors as well as providing many other benefits.
This entry-level training course is designed to bring you quickly up to speed with the basic features and processes involved in performing C# based test-driven development using xUnit. It covers the key principles and practices of test-driven development, which you can then utilize within your own software engineering projects.
In this course, you'll be guided through an end-to-end sample project in which we use test-driven development to build, test, and package a .NET Core 3.1 C# based library that will provide an API to convert Bitcoins into foreign currency.
If you have any feedback relating to this course, please contact us at support@cloudacademy.com.
Learning Objectives
- Understand the fundamentals of TDD and xUnit
- Use TDD to build, test, and package a .NET Core 3.1 C# based library
- Perform TDD by performing Red-Green-Refactor iterations
- Learn how to use TDD to build, test, and package a .NET Core 3.1 C# based library
- Learn how to create unit tests using the xUnit testing framework
Intended Audience
- Software developers interested in using TDD to build .NET Core applications using C#
Prerequisites
To get the most out of this course, you should have a basic understanding of .NET Core and the C# programming language, as well as software development and the software development life cycle (SDLC).
Resources
Welcome back. In this lesson, I'm going to provide you with a quick review of unit testing, what it is and how you go about performing it yourself. I'll reveal the basic structure of a unit test as well as highlighting basic practices for naming and building your own, let's begin.
Up until now, I've largely referred to test-driven development test being just that, test. But what are they really in a real world software engineering project? Most often, test-driven development tests are implemented as unit test. A test that is focused on testing a discreet or smaller part of a larger system and done so in an isolated manner.
A unit test in the context of this course tests and validates the run-time behavior of a class. A C# class is, as we all know, nothing more than a template for constructing objects at runtime. A class typically contains methods and properties, which manage both the state and behavior within an object instantiation of that class. Therefore, unit tests that target and test C# classes need to test and validate each of the individual methods.
Unit tests should focus on testing the behaviors of a class and nothing more. By this very definition, they should execute fast, mostly in sub time and entirely in memory. Again, it's not uncommon to execute hundreds of unit tests per second. If a unit test instead takes several seconds or worst, whole minutes to execute, then it's probably not a unit test, but instead a misplaced integration test meaning this test should be moved elsewhere. Examples of this include a test involving network communication, a test reading or writing to a file system, a test transacting with a database, or a test involving console input or user interaction.
Again, to reiterate, unit tests are meant to execute fast, and I mean really fast. If they don't, then they're probably aren't unit tests. When it comes to performing C# based unit testing within Dotnet, there are many unit testing frameworks available to choose from. Rather than calling all of them out individually, I'll quickly review the three most popular ones.
One, MSTest, provided by Microsoft and ships by default with the Dotnet frameworks. Two, NUnit, is an open source unit testing framework, inspired imported from Java's JUnit. And three, xUnit, another open source unit testing framework with one of its distinguishing concepts being that it allows you to divide your tests and effects in theories, which differentiate between always true and true for the right data, respectively.
Each unit testing framework has its own pros and cons. Regardless of them, they all accomplish the core objective that is providing with the ability to quickly and easily write unit tests. In this course, I've chosen to go with xUnit, xUnit provides an intuitive interface for crafting unit tests. It's quick and easy to install and just as easy to begin creating unit tests.
In terms of learning xUnit, the good news is, is that it's highly intuitive and therefore, very easy to learn is your witness when observing the given demonstrations. Let's now take a quick look at what a unit test actually looks like.
In the given example displayed here, we have created a single unit test that tests the static add method declared within the calculator class. The unit test itself has been developed using the xUnit unit testing framework, which I'll go into deeper in the next lesson. But for now, I wanted to highlight that a unit test is most often structured using the Arrange Act Asset pattern.
Detailing the individual parts of this pattern, we have the arrange part used to set up the code under test. Here, objects being tested get instantiated marks if used are set up and expectations are set. The act part. this section executes the method and behavior being tested. And finally, the assert part. Here, assertions are defined to check whether specified expectations were met or not.
Following the Arrange Act Asset pattern, we'll help other members of your team to quickly understand the intention of your unit test. Moving along, when it comes to naming standards for your unit tests, there's no hard and fast rule which is enforced at compile time. However, do consider carefully the way you name your unit tests, particularly the method names. Your test method names can help others who are unfamiliar with your unit tests to understand the specific unit under test and expected behaviors and outcomes for particular states, et cetera.
Now, before I promote an actual test naming strategy, let me first review some motives with such a strategy. One, test names should make reference to the method under test. Two, test names should express the overall intention of the method under test. Three, test names should make reference to both the inputs or state and the result and output or expectation. And four, test names collectively should describe the overall behavior of the unit or class under test.
Now that I've covered off the motives for a successful unit testing naming strategy, let's formalize it by stating the naming strategy, which I'll use in the later given test driven development coding demonstrations. For the demonstrations provided within this course, the particular naming strategy I'll use will be the following.
This naming strategy breaks down from left to right. The name of the method being tested underscore the state being imported or tested within the method underscore, and then the expected resulting behavior. With this naming strategy, it should be very clear to someone new to the project exactly what is being tested.
Here are some examples using that naming strategy for a class that implements a square root method. This unit test naming strategy makes it very clear as to what is being tested. The state the test is acting on in the expected outcomes. Okay, that completes this lesson on unit tests.
In this lesson, you learned about what a unit test is. You learned that a unit test is designed to test the runtime behaviors of a class and nothing more. You learned that unit tests should run very fast and close to subsequent time. You learned that unit tests are best structured using the Arrange Act Assert pattern, and you learn that naming your unit test methods appropriately will help others to understand quickly and easily the intention of your unit test.
Go ahead now and close this lesson and I'll see you shortly in the next one.
Jeremy is a Content Lead Architect and DevOps SME here at Cloud Academy where he specializes in developing DevOps technical training documentation.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 25+ years. In recent times, Jeremy has been focused on DevOps, Cloud (AWS, Azure, GCP), Security, Kubernetes, and Machine Learning.
Jeremy holds professional certifications for AWS, Azure, GCP, Terraform, Kubernetes (CKA, CKAD, CKS).