Now that we have a basic understanding of Test-Driven Development, including how it works in conjunction with automated testing, let's revisit the leap year application we started in the pre-work. We will write our specs and translate them to unit tests, which we'll run using a tool called MSTest.
MSTest is a unit testing framework that allows us to set up test cases. We can tell our test to expect a specific result and then our test will let us know whether that expected result is achieved or not. Good unit tests should cover not just expected inputs but also edge cases. Edge cases can occur when an extreme or unusual parameter is passed into a method. For example, a method that calculates a person's overall health partly based on their age may not correctly account for a person that is 117 years old.
Let's walk through how to set up MSTest in our own C# projects.
Calendar.Solutiondirectory create two additional subdirectories:
The directory structure will look like this:
Calendar.Solution ├── Calendar └── Calendar.Tests
Calendar.Solutionis the parent directory where all our code lives. It actually contains two versions of the same project:
Calendarwill hold our application logic. This is sometimes referred to as the production project because it contains the code that will actually run when we launch the program.
Calendar.Testswill hold our automated tests. It's the test version of our project. We should always append a
.Testssuffix to this directory.
Next we need special
Create a file in the
Calendar.Solution/Calendar directory called
Calendar.csproj. The resulting structure will look like this:
Calendar.Solution ├── Calendar │ └── Calendar.csproj └── Calendar.Tests
Place the standard boilerplate in the new
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> </Project>
Note that this doesn't have the output type in the Property Group that we would include in a project with a front end, since this example is to show the setup for a project purely for the purposes of testing. As such, this will throw an error if you try to include a front end and run
dotnet run, since there is no executable file for it to run. If you want to create an executable program with a user interface, you will need to add
<OutputType>Exe</OutputType> to the
<PropertyGroup> element as in previous projects.
Our production project and test project are technically two separate projects, which means that each needs its own
Let's add a
Calendar.Tests.csproj file to the
Calendar.Tests subdirectory. The resulting file structure will look like this:
Calendar.Solution ├── Calendar │ └── Calendar.csproj └── Calendar.Tests └── Calendar.Tests.csproj
Add the following code to
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" /> <PackageReference Include="MSTest.TestAdapter" Version="1.3.2" /> <PackageReference Include="MSTest.TestFramework" Version="1.3.2" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Calendar\Calendar.csproj" /> </ItemGroup> </Project>
.csproj file looks very different from the boilerplate
.csproj files we've used so far. This is because a
.csproj file is used to import necessary external packages. In this case, we need packages to run our tests.
<ItemGroup>area, we import three packages, each in special
Microsoft.NET.Test.Sdkimports functionality to set up and build our testing environment;
MSTest.TestAdapterwill run our tests for us;
MSTest.TestFrameworkallows us to create test classes and methods and to use other built-in code to construct tests.
<ItemGroup>element, our test project references our main production project. This allows our tests to locate and use the namespaces of our production project without interfering with it.
Now that our
.csproj files are complete, we need to add these tools to our project. Navigate into the
Calendar.Tests folder in the command line and run the following:
$ dotnet restore
This command tells NuGet, a free open source package manager that comes with .NET, to retrieve and install the packages we listed in
.csproj in our application. It's much easier than manually locating and installing packages like we did in Intro!
This command automatically creates new
obj directories in our
Calendar.Tests project, each containing multiple files, looking something like this:
Calendar.Solution ├── Calendar │ ├── Calendar.csproj │ └── obj │ ├── Calendar.csproj.nuget.cache │ ├── Calendar.csproj.nuget.g.props │ ├── Calendar.csproj.nuget.g.targets │ └── project.assets.json └── Calendar.Tests ├── Calendar.Tests.csproj └── obj ├── Calendar.Tests.csproj.nuget.cache ├── Calendar.Tests.csproj.nuget.g.props ├── Calendar.Tests.csproj.nuget.g.targets └── project.assets.json
These files contain detailed references and configuration instructions that our program will reference later on. Depending on your machine, you may have differently named files in this folder. They contain special internal code and we won't need to interact with these files for the most part. For now, simply leave them alone. If these files are accidentally deleted or modified, they can be restored with
$ dotnet restore command.
Keep in mind that anytime we update a
.csproj file, we need to run
> dotnet restore to download and install updated packages.
It's also essential to ensure that we run console commands within the correct project. In this case, we need to be running the command in our
Calendar.Tests folder. If our terminal is located in the root directory of the project, we can run commands that specify an individual project like this:
$ dotnet restore Calendar.Tests
Now that our project directory and necessary packages are in place, let's start building our tests.
MSTest: A unit testing framework that allows us to set up test cases.
NuGet: Free open source package manager that comes with .NET.
Unit test: A test to verify a single behavior.
Edge case: When an extreme or unusual parameter is passed into a method.
csproj file for tests:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" /> <PackageReference Include="MSTest.TestAdapter" Version="1.3.2" /> <PackageReference Include="MSTest.TestFramework" Version="1.3.2" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\ProjectName\ProjectName.csproj" /> </ItemGroup> </Project>
.csproj file is added, run the following in test directory:
$ dotnet restore
Directory structure for a project with tests. Note that all files in
obj folders are generated by .NET.
ProjectName.Solution ├── ProjectName │ ├── ProjectName.csproj │ └── obj │ ├── ProjectName.csproj.nuget.cache │ ├── ProjectName.csproj.nuget.g.props │ ├── ProjectName.csproj.nuget.g.targets │ └── project.assets.json └── ProjectName.Tests ├── ProjectName.Tests.csproj └── obj ├── ProjectName.Tests.csproj.nuget.cache ├── ProjectName.Tests.csproj.nuget.g.props ├── ProjectName.Tests.csproj.nuget.g.targets └── project.assets.json
Lesson 5 of 20
Last updated April 14, 2022