Home Web Development Pragmatic Test-Driven Development in C# and .NET

Pragmatic Test-Driven Development in C# and .NET

By Adam Tibi
books-svg-icon Book
eBook $33.99 $22.99
Print $41.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $33.99 $22.99
Print $41.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Chapter 1: Writing Your First TDD Implementation
About this book
Test-driven development is a manifesto for incrementally adding features to a product but starting with the unit tests first. Today’s project templates come with unit tests by default and implementing them has become an expectation. It’s no surprise that TDD/unit tests feature in most job specifications and are important ingredients for most interviews and coding challenges. Adopting TDD will enforce good design practices and expedite your journey toward becoming a better coding architect. This book goes beyond the theoretical debates and focuses on familiarizing you with TDD in a real-world setting by using popular frameworks such as ASP.NET Core and Entity Framework. The book starts with the foundational elements before showing you how to use Visual Studio 2022 to build an appointment booking web application. To mimic real-life, you’ll be using EF, SQL Server, and Cosmos, and utilize patterns including repository, service, and builder. This book will also familiarize you with domain-driven design (DDD) and other software best practices, including SOLID and FIRSTHAND. By the end of this TDD book, you’ll have become confident enough to champion a TDD implementation. You’ll also be equipped with a business and technical case for rolling out TDD or unit testing to present to your management and colleagues.
Publication date:
September 2022
Publisher
Packt
Pages
372
ISBN
9781803230191

 

Writing Your First TDD Implementation

I’ve always liked books that start with a quick end-to-end demo about the proposed subject before diving into the details. That gives me a sense of what I am going to learn. I wanted to share with you the same experience by beginning this book with a tiny application.

Here, we will simulate minimal business requirements, and while implementing them, we will touch on unit testing and test-driven development (TDD) concepts. Don’t worry if a concept is not clear or requires further explanation, as this chapter purposely skims over topics to give you a flavor. By the end of the book, we will have covered all the concepts that were skimmed over.

Also, note that we will use the terms unit testing and TDD interchangeably with little distinction. The difference will be clearer by Chapter 5, Test-Driven Development Explained.

In this chapter, you will cover the following topics:

  • Choosing your integrated development environment (IDE)
  • Building a solution skeleton with unit testing
  • Implementing requirements with TDD

By the end of the chapter, you will be comfortable writing basic unit tests using xUnit and have a fair understanding of what TDD is.

 

Technical requirements

The code for this chapter can be found at the following GitHub repository:

https://github.com/PacktPublishing/Pragmatic-Test-Driven-Development-in-C-Sharp-and-.NET/tree/main/ch01

 

Choosing your IDE

From a TDD perspective, different IDEs will affect your productivity. TDD implementation can be boosted by IDEs that have rich code refactoring and code generation capabilities, and selecting the right one will reduce repetitive—and potentially boring—tasks.

In the following sections, I have presented three popular IDEs with C# support: Visual Studio (VS), VS Code, and JetBrains Rider.

Microsoft VS

This chapter and the rest of the book will use VS 2022 Community Edition—this should also work with the Professional and Enterprise editions. Individual developers can use VS Community Edition for free to create their own free or paid applications. Organizations can also use it under some restrictions. For the full license and product details, visit https://visualstudio.microsoft.com/vs/community/.

If you have an earlier version of VS and do not want to upgrade, then you can have VS 2022 Community Edition installed side by side with previous versions.

Both the Windows and Mac editions of VS 2022 have the required tools to build our code and run the tests. I have done all the projects, screenshots, and instructions in this book using the Windows edition. You can download VS from https://visualstudio.microsoft.com/downloads/.

When installing VS, you will need at least the ASP.NET and web development box selected to be able to follow along with the book, as illustrated in the following screenshot:

Figure 1.1 – VS installation dialog

Figure 1.1 – VS installation dialog

If you have VS previously installed, you can check if ASP.NET and web development is already installed by following these steps:

  1. Go to Windows Settings | Apps | Apps & features.
  2. Search for Visual Studio under App list.
  3. Select the vertical ellipsis (the three vertical dots).
  4. Select Modify, as shown in the following screenshot:
Figure 1.2 – Modifying VS installation

Figure 1.2 – Modifying VS installation

VS is big, as it contains plenty of components to install. Also, after installation, it is the slowest to load, in comparison with Rider and VS Code.

ReSharper

JetBrains ReSharper is a popular commercial plugin for VS. ReSharper adds multiple features to VS; however, from a TDD standpoint, we are interested in the following aspects:

  • Refactoring: ReSharper adds many refactoring features that come in handy when you reach the refactoring stage of TDD.
  • Code generation: Generating code with ReSharper is particularly useful when creating your unit tests first, then generating code after.
  • Unit testing: ReSharper supercharges the unit testing tools in VS and has support for more unit testing frameworks.

ReSharper is a subscription-based product with a 30-day trial. I would recommend you to start first with VS without ReSharper, then add it later when you are familiar with the capabilities of VS so that you recognize the benefits of adding ReSharper.

Note

Each new release of VS adds additional code refactoring and code generation capabilities similar to those of ReSharper. However, as of now, ReSharper has more advanced features.

In this book, the discussion on ReSharper will be limited to this section. You can download ReSharper here: https://www.jetbrains.com/resharper/.

JetBrains Rider

JetBrains, the company behind Rider, is the same company behind the popular ReSharper VS plugin. If you have chosen JetBrains Rider for your .NET development, then you have all the features that are required in this book. Rider has the following:

  • A powerful unit test runner that competes with Test Explorer of VS
  • Feature-rich code refactoring and code generation capabilities with more advanced features than those of VS 2022

The aforementioned points are crucial for building a system TDD-style; however, I have chosen VS for this book rather than Rider. Although the instructions in this book are meant for VS 2022, they can be applied to Rider, taking into consideration that Rider has a different menu system and shortcuts.

Note

VS .NET (VS release with .NET support) was released in February 2002, while Rider is more recent and was released in August 2017; so, VS is more established between .NET developers. I have nominated VS for this book over Rider for this reason.

You can download Rider here: https://www.jetbrains.com/rider/.

VS Code

If you are a fan of VS Code, you will be pleased to know that Microsoft added native support for visual unit testing (which is essential for TDD) in July 2021, with the version 1.59 release.

VS Code is a lightweight IDE—it has good native refactoring options and a bunch of third-party refactoring plugins. The simplicity and elegance of VS Code attract many TDD practitioners, but the available C# features—especially those used in TDD—are not as advanced as those of VS or Rider.

I will be using VS in this book, but you can adapt the examples to VS Code where relevant. To download VS Code, you can visit https://visualstudio.microsoft.com/downloads/.

.NET and C# versions

VS 2022 comes with .NET 6 and C# 10 support. This is what we will be using for the purposes of this chapter and the rest of the book.

I initiated a small poll to gather some public opinion in my LinkedIn group—you can see the results here:

Figure 1.3 – LinkedIn IDE poll results

Figure 1.3 – LinkedIn IDE poll results

As you can see, VS has the highest usage of 58%, with 18% who use the ReSharper plugin with VS, then Rider comes second at 24%, then in third place comes VS Code with 18%. However, given that this is only 45 votes, it is meant to give you an indication and would definitely not reflect the market.

Picking the right IDE is a debatable subject between developers. I know that every time I ask a developer practicing TDD about their chosen IDE, they would swear by how good their IDE is! In conclusion, use the IDE that makes you more productive.

 

Building a solution skeleton with unit testing

Now that we’ve got the technical requirements out of the way, it’s time to build our first implementation. For this chapter, and to keep the focus on TDD concepts, let’s start with simple business requirements.

Let’s assume that you are a developer, working for a fictitious company called Unicorn Quality Solutions Inc. (UQS), which produces high-quality software.

Requirements

The software teams in UQS follow an agile approach and describe business requirements in terms of a user story.

You are working on a math library that is packaged to be consumed by other developers. You can think of this as if you are building a feature within a NuGet library to be used by other applications. You’ve picked a user story to implement, as outlined here:

Story Title:

Integers Division

Story Description:

As a math library client, I want a method to divide two integers

Acceptance Criteria:

Supports an Int32 input and a decimal output

Supports high-precision return with no/minimal rounding

Supports dividing divisible and indivisible integers

Throws a DivideByZeroException when dividing by 0

Creating a project skeleton

You will need two C# projects for this story. One is the class library that will contain the production code and a second library for unit testing the class library.

Note

Class libraries enable you to modularize functionality that can be used by multiple applications. When compiled, they will generate dynamic-link library (DLL) files. A class library cannot run on its own, but it can run as part of an application.

If you have not worked with a class library before, for the purposes of this book, you can treat it like a console app or a web app.

Creating a class library project

We are going to create the same projects set up in two ways—via the graphical user interface (GUI) and via the .NET command-line interface (CLI). Choose what you prefer or what you are familiar with.

Via the GUI

To create a class library, run VS and then follow these steps:

  1. From the menu, select File | New | Project.
  2. Look for Class Library (C#).
  3. Select the rectangle containing Class Library (C#) | hit Next. The Add a new project dialog will display, as follows:
Figure 1.4 – Finding the Class Library (C#) project template

Figure 1.4 – Finding the Class Library (C#) project template

Important Note

Make sure you can see the C# tag in the box and do NOT select the Class Library (.NET Framework) item. We are using .NET (not classical .NET Framework).

  1. In the Configure your new project dialog, type Uqs.Arithmetic in the Project name field and UqsMathLib in the Solution name field, then hit Next. The process is illustrated in the following screenshot:
Figure 1.5 – Configure your new project dialog

Figure 1.5 – Configure your new project dialog

  1. In the Additional information window, select .NET 6.0 (Long-term support) and hit Create. The process is illustrated in the following screenshot:
Figure 1.6 – Additional information

Figure 1.6 – Additional information

We now have a class library project within the solution, using the VS GUI.

Via the CLI

If you prefer to create your project via the CLI, here are the required commands:

  1. Create a directory called UqsMathLib (md UqsMathLib).
  2. Navigate to this directory via your terminal (cd UqsMathLib), as illustrated in the following screenshot:
Figure 1.7 – Command Prompt showing the commands

Figure 1.7 – Command Prompt showing the commands

  1. Create a solution file (.sln) that will be generated with the same name as the directory—that is, UqsMathLib.sln—by running the following command:
    dotnet new sln
  2. Create a new class library called Uqs.Arithmetic in a directory with the same name and use .NET 6.0. Here’s the code you need to execute:
    dotnet new classlib -o Uqs.Arithmetic -f net6.0 
  3. Add the newly created project to the solution file by running the following command:
    dotnet sln add Uqs.Arithmetic 

We now have a class library project within the solution, using the CLI.

Creating a unit testing project

Currently, we have a solution with one class library project. Next, we want to add the unit test library to our solution. For this, we will use xUnit Test Project.

xUnit.net is a free, open source, unit testing tool for .NET. It is licensed under Apache 2. VS natively supports adding and running xUnit projects, so no special tool or plugin is needed to use xUnit.

We will be going into more details about xUnit in Chapter 3, Getting Started with Unit Testing.

We will follow a common convention for naming unit test projects: [ProjectName].Tests.Unit. Thus, our project will be called Uqs.Arithmetic.Tests.Unit.

We will create a unit test project in two ways, so you can pick whatever suits you best.

Via the GUI

To create a unit testing project, go to Solution Explorer in VS, then follow these steps:

  1. Right-click on the solution file (UqsMathLib).
  2. Go to Add | New Project…, as illustrated in the following screenshot:
Figure 1.8 – Creating a new project in a solution

Figure 1.8 – Creating a new project in a solution

  1. Look for xUnit Test Project | hit Next.
  2. Set the Project name value as Uqs.Arithmetic.Tests.Unit.
  3. Hit Next | select .NET 6.0 | hit Create.

You have created a project via the VS GUI, but we still need to set the unit test project to have a reference to the class library. To do so, follow these steps:

  1. In VS Solution Explorer, right-click on Dependencies in Uqs.Arithmetic.Tests.Unit.
  2. Select Add Project Reference….
  3. Tick Uqs.Arithmetic and hit OK.

We now have our solution fully constructed via the VS GUI. You may choose to do the same GUI steps in the CLI instead. In the next section, we will do exactly that.

Via the CLI

Currently, we have a solution with one class library project. Now, we want to add the unit test library to our solution.

Create a new xUnit project called Uqs.Arithmetic.Tests.Unit in a directory with the same name and use .NET 6.0. Here’s the code you need to execute:

dotnet new xunit -o Uqs.Arithmetic.Tests.Unit -f net6.0

Add the newly created project to the solution file by running the following command:

dotnet sln add Uqs.Arithmetic.Tests.Unit

We now have two projects in our solution. As the unit test project will be testing the class library, the project should have a reference to the class library.

You have created a project via the CLI, but we still need to set the unit test project to have a reference to the class library. To do so, add a project reference from Uqs.Arithmetic.Tests.Unit to Uqs.Arithmetic, like so:  

dotnet add Uqs.Arithmetic.Tests.Unit reference 
   Uqs.Arithmetic

We now have our solution fully constructed via the CLI.

Final solution

Whichever method you’ve used to create the solution—either the VS GUI or the CLI—you should now have the same files created. Now, you can open the solution in VS, and you’ll see this:

Figure 1.9 – The final created solution structure

Figure 1.9 – The final created solution structure

To start from a clean slate, delete Class1.cs as we won’t be using it—it was added automatically by the template.

The logical structure of our two projects looks like this:

Figure 1.10 – Projects’ logical structure

Figure 1.10 – Projects’ logical structure

What we’ve created so far are two projects: one that will be shipped to production at some stage (Uqs.Arithmetic) and one to test this project (Uqs.Arithmetic.Tests.Unit). The solution file links the two projects together.

Now that we have finished the less fun part of building the project skeleton and setting the dependencies, we can now start the more fun one, which is directly related to unit testing.

Familiarizing yourself with built-in test tools

We have reached the stage where we need to look into how to discover and execute tests, and to do that, we need to understand which tools are available to us.

We have code that is already generated by the xUnit template—look at the code inside UnitTest1.cs, as displayed here:

using Xunit;
namespace Uqs.Arithmetic.Tests.Unit;
public class UnitTest1
{
    [Fact]
    public void Test1()
    {
    }
}

This is a normal C# class. Fact is an attribute from xUnit. It simply tells any xUnit-compatible tool that the method decorated with Fact is a unit test method. xUnit-compatible tools such as Test Explorer and .NET CLI Test Command should be able to find this method in your solution and run it.

Following the trend of the previous sections, we will utilize the available test tools in two ways—via the VS GUI and via the CLI.

Via the GUI

VS comes with a GUI as a test runner to discover and execute tests—it is called Test Explorer. To see how a test runner would discover test methods, from the menu, go to Test | Test Explorer. You will see the following screen:

Figure 1.11 – Test Explorer showing unexecuted tests

Figure 1.11 – Test Explorer showing unexecuted tests

As you can see, it detected all the tests in our solution, showing the tests in a Project Name > Namespace > Class > Method hierarchy. Also, you can see that the test hierarchy is grayed out and has an exclamation mark. This is a sign that the tests were never run. You can click on the upper-left Run button (shortcut keys: Ctrl + R, T. This is pressing and holding Ctrl then pressing R and quickly switching from R to T) and run this test. This will build your project and will execute the code within the methods decorated with Fact. The results are shown here:

Figure 1.12 – Test Explorer showing executed test results

Figure 1.12 – Test Explorer showing executed test results

Don’t expect anything fancy as we have an empty shell, but at least the tests will turn green and you will know that your setup is working. You can similarly discover and execute tests using the CLI.

Via the CLI

You can also execute the same test by using Command Prompt, going to the solution directory, and executing the following command:

dotnet test

This is what you are going to get:

Figure 1.13 – .NET Test command discovering and executing tests

Figure 1.13 – .NET Test command discovering and executing tests

Running commands such as this will come in handy later on when we want to automate test running.

 

Implementing requirements with TDD

Before writing any code, it makes sense that we understand some terminologies and conventions to tune our brain on unit test-related keywords. So, we will briefly touch on system under test (SUT), red/green tests, and Arrange-Act-Assert (AAA). More details on these terminologies will follow in later chapters, but for now, we will cover the minimum to get a few tests running.

While we are learning about terminology and conventions, we will ease into our implementation. One thing that you might find new or unordinary is writing a unit test first, then writing production code later. This is one main aspect of TDD, and you will first experience it in this section.

SUT

We refer to the code that you usually write to build a product as production code. Typical object-oriented (OO) production code looks like this:

public class ClassName
{
    public Type MethodName(…)
    {
        // Code that does something useful
    }
    // more code
}

When we test this code, the unit test will call MethodName and assess the behavior of this method. When MethodName is executed, it may call other parts of the class and may use/call other classes. The code executed by MethodName is called SUT or code under test (CUT). However, the term SUT is used more often.

The SUT will have an entry point that will be executed by the unit tests. The entry point is usually the method that we are calling from the unit tests. The following screenshot should clarify the idea of a SUT and a SUT entry point:

Figure 1.14 – Unit tests operating on a SUT

Figure 1.14 – Unit tests operating on a SUT

In the previous screenshot, you can see multiple unit tests calling the same SUT entry point. A detailed discussion of SUT is available in Chapter 3, Getting Started with Unit Testing.

Testing class

A typical unit testing class uses the same names from the SUT, by convention. This is what a typical unit testing class looks like:

public class ClassNameTests
{
    [Fact]
    public void MethodName_Condition1_Expectation1()
    {
        // Unit Testing Code that will call MethodName
    }
    // Other tests…
    [Fact]
    public void MethodName_ConditionN_ExpectationN()
    {
        // Unit Testing Code that will call MethodName
    }
    …
}

Notice that the ClassName and MethodName methods in the two previous snippets are not a coincidence. We want them to be the same, again, by convention. To start forming our test class, we need to design the class name and the method name.

Class name

From the requirements, we will need a class that will contain all our division methods, so let’s simply call the class Division; and if we were to create a unit test class to test the Division class, our unit test name would be called DivisionTests. Next, we will rename the UnitTest1 class DivisionTests and rename the file as well so that it appears as DivisionTests.cs.

Tip

You can set your text cursor anywhere within the class name in the source code (in the previous case, it was UnitTest1) and hit Ctrl + R, R (hold Ctrl then press R quickly twice). Type the new name DivisionTests and hit Enter. This will also rename the file if the Rename symbol’s file checkbox is ticked.

Method name

Luckily, the requirements are simple, so our method name will simply be Divide. Divide will be accepting two integer (int32) arguments, per the requirements, and returns a decimal value. We will go ahead and refactor our existing unit test from Test1 to Divide_Condition1_Expectation1.

Note

Arithmetic terminology-naming reminder: If we have 10 / 5 = 2, then 10 is the dividend, 5 is the divisor, and 2 is the quotient.

Conditions and expectations

When we test, we are setting a condition and defining what we expect when this condition is met. We start with the core case, also known as the positive path or the happy path. We finish all the positive paths first before going to other cases. Our mission in unit tests boils down to determining the condition and its expectation and having a unit test for every combination.

To show the relationship between the method we are testing (the method in our SUT) and the associated condition and expectation, we will employ a well-used convention, as illustrated in the following code snippet:

[Fact]
public void MethodName_Condition_Expectation()
{
…

Here are random examples of unit test method names to familiarize you with the previous convention:

  • SaveUserDetails_MissingEmailAddress_EmailIsMissing
  • ValidateUserCredentials_HashedPasswordDoesntMatch_False
  • GetUserById_IdDoesntExist_UserNotFoundException

We will see more examples while designing our unit tests.

The core requirement is dividing two integers. The straightforward and simplest implementation is dividing two divisible integers and getting back a whole number. Our condition is divisible integers and we expect a whole number. Now, we should update the signature of our unit test to  Divide_DivisibleIntegers_WholeNumber and write the body of the test method, as follows:

[Fact]
public void Divide_DivisibleIntegers_WholeNumber()
{
    int dividend = 10;
    int divisor = 5;
    decimal expectedQuotient = 2;
    decimal actualQuotient = Division.Divide(dividend, 
        divisor);
    Assert.Equal(expectedQuotient, actualQuotient);
}

This code doesn’t compile as the Division class doesn’t exist at this stage, and we know that already as we have a squiggly line under Division. This is one of the rare occasions where not being able to compile due to a missing class is good. This indicates that our test has failed, which is also good!

While it does look silly that the test has failed because the code doesn’t compile as the Division SUT class is missing, this means that there is no SUT code yet. In Chapter 5, Test-Driven Development Explained, we will understand the reason behind considering the no-compilation case.

Assert is a class from the xUnit library. The Equal static method has many overloads, one of which we are using here:

public static void Equal<T>(T expected, T actual) 

When run, this method will flag to the xUnit framework if what we expect and what we’ve actually got are equal. When we run this test, if the result of this assertion is true, then the test has passed.

Red/green

Failure is what we were looking for. In later chapters, we will discuss why. For now, it is sufficient to know that we need to start with a failed build (compilation) or failed test (failed assertion), then change that to a passed one. The fail/pass is also known as the red/green refactor technique, which mimics the idea of bad/good and stop/go.

We need to add the Division class and the Divide method and write the minimal code to make the test pass. Create a new file called Division.cs in the Uqs.Arithmetic project, like this:

namespace Uqs.Arithmetic;
public class Division
{
    public static decimal Divide(int dividend, int divisor)
    {
        decimal quotient = dividend / divisor;
        return quotient;
    }
}

Tip

You can create a class by placing the text cursor anywhere within the class name (in the previous case, it was Division) and hitting Ctrl + . (hold down the Ctrl key and then press .). Select Generate new type…, then from the Project dropdown, select Uqs.Arithmetic, and then hit OK. Then, to generate the method, place your text cursor on Divide and hit Ctrl + ., select Generate method ‘Division.Divide’, and then you get the method shell in Division ready for your code.

It is important to remember that dividing two integers in C# will return an integer. I have seen senior developers fail to remember this, which led to bad consequences. In the code that we implemented, we have only covered the integers division that will yield a whole quotient. This should satisfy our test.

We are now ready to run our test with Test Explorer, so hit Ctrl + R, A, which will build your projects, then run all the tests (currently one test). You’ll notice that Test Explorer indicates green, and there is a green bullet with a tick mark between the test name and the Fact attribute. When clicked, it will show you some testing-related options, as illustrated in the following screenshot:

Figure 1.15 – VS unit testing balloon

Figure 1.15 – VS unit testing balloon

For the sake of completion, the full concept name is red/green/refactor, but we won’t be explaining the refactor bit here and will leave this for Chapter 5, Test-Driven Development Explained.

The AAA pattern

Unit testing practitioners noticed that test code format falls into a certain structure pattern. First, we declare some variables and do some preparations. This stage is called Arrange.

The second stage is when we invoke the SUT. In the previous test, it was the line on which we called the Divide method. This stage is called Act.

The third stage is where we validate our assumption—this is where we have the Assert class being used. This stage is, not surprisingly, called Assert.

Developers usually divide each unit test with comments to denote these three stages, so if we apply this to our previous unit test, the method would look like this:

[Fact]
public void Divide_DivisibleIntegers_WholeNumber()
{
    // Arrange
    int dividend = 10;
    int divisor = 5;
    decimal expectedQuotient = 2;
    // Act
    decimal actualQuotient = Division.Divide(dividend,
       divisor);
    // Assert
    Assert.Equal(expectedQuotient, actualQuotient);
}

You can learn more about the AAA pattern in Chapter 3, Getting Started with Unit Testing.

More tests

We haven’t finished implementing the requirements. We need to add them iteratively, by adding a new test, checking that it fails, implementing it, then making it pass, and then repeating it!

We are going to add a few more tests in the next sections to cover all the requirements, and we are also going to add some other tests to increase the quality.

Dividing two indivisible numbers

We need to cover a case where two numbers are not divisible, so we add another unit testing method under the first one, like so:

[Fact]
public void Divide_IndivisibleIntegers_DecimalNumber()
{
    // Arrange
    int dividend = 10;
    int divisor = 4;
    decimal expectedQuotient = 2.5m;
    …
}

This unit test method is similar to the previous one, but the name of the method has changed to reflect the new condition and expectation. Also, the numbers have changed to fit the new condition and expectation.

Run the test by employing any of the following methods:

  • Clicking the blue bullet that appears below Fact, then clicking Run
  • Opening Test | Test Explorer, selecting the new test name code, and clicking the Run button
  • Pressing Ctrl + R, A, which will run all tests

You will notice that the test will fail—this is good! We have not implemented the division that will yield a decimal yet. We can go ahead and do it now, as follows:

decimal quotient = (decimal)dividend / divisor;

Note

Dividing two integers in C# will return an integer, but dividing a decimal by an integer returns a decimal, therefore you almost always have to cast the dividend or the divisor—or both—to a decimal.

Run the test again, and this time it should pass.

Division-by-zero test

Yes—bad things happen when you divide by zero. Let’s check whether our code can handle this, as follows:

[Fact]
public void Divide_ZeroDivisor_DivideByZeroException()
{
    // Arrange
    int dividend = 10;
    int divisor = 0;
    
    // Act
    Exception e = Record.Exception(() => 
        Division.Divide(dividend, divisor));
    // Assert
    Assert.IsType<DivideByZeroException>(e);
}

The Record class is another member of the xUnit framework. The Exception method records whether the SUT has raised any Exception object and returns null if there is none. This is the method’s signature:

public static Exception Exception(Func<object> testCode)

IsType is a method that compares the class type between the angle brackets to the class type of the object that we passed as an argument, as illustrated in the following code snippet:

public static T IsType<T>(object @object)

When you run this test, it will pass! My first impression would be one of suspicion. The problem is that when it passes without writing explicit code, we don’t know yet whether this is a true or a coincidental pass—a false positive. There are many ways to validate whether this pass is incidental; the quickest way—for now—is to debug the code of Divide_ZeroDivisor_DivideByZeroException.

Click the Test Bullet, and then click the Debug link, as illustrated in the following screenshot:

Figure 1.16 – The Debug option in the unit testing balloon

Figure 1.16 – The Debug option in the unit testing balloon

You will hit the exception directly, as illustrated in the following screenshot:

Figure 1.17 – Exception dialog

Figure 1.17 – Exception dialog

You’ll notice that the exception is happening at the right place at the division line, so this is what we actually wanted. While this method violated our initial attempt of red/green, having a pass immediately is still a genuine case that you would encounter in day-to-day coding.

Testing extremes

The story did not mention testing the extremes, but as a developer, you know that most software bugs come from edge cases.

You want to build more confidence in your existing code, and you want to make sure that it can handle extremes well, as you’d expect it to.

The extreme values of an int data type can be obtained by these two constant fields of int:

  • int.MaxValue =
  • int.MinValue =

What we need to do is to test the following cases (note that we will only test for 12 decimal digits):

  • int.MaxValue / int.MinValue = -0.999999999534  
  • (-int.MaxValue) / int.MinValue = 0.999999999534
  • int.MinValue / int.MaxValue = -1.000000000466
  • int.MinValue / (-int.MaxValue) = 1.000000000466

So, we will need four unit tests to cover each case. However, there is a trick available in most unit test frameworks, including xUnit. We don’t have to write four unit tests—we can do this instead:

[Theory]
[InlineData( int.MaxValue,  int.MinValue, -0.999999999534)]
[InlineData(-int.MaxValue,  int.MinValue,  0.999999999534)]
[InlineData( int.MinValue,  int.MaxValue, -1.000000000466)]
[InlineData( int.MinValue, -int.MaxValue,  1.000000000466)]
public void Divide_ExtremeInput_CorrectCalculation(
    int dividend, int divisor, decimal expectedQuotient)
{
    // Arrange
    // Act
    decimal actualQuotient = Division.Divide(dividend, 
        divisor);
    // Assert
    Assert.Equal(expectedQuotient, actualQuotient, 12);
}

Notice that now we have Theory rather than Fact. This is xUnit’s way of declaring that the unit test method is parametrized. Also, notice that we have four InlineData attributes; as you will have already figured out, each one of them corresponds to a test case.

Our unit test method and the InlineData attributes have three parameters. When running the unit tests, each parameter will map to the corresponding unit test method’s parameter in the same order. The following screenshot shows how each parameter in the InlineData attribute corresponds to a parameter in the Divide_ExtremeInput_CorrectCalculation method:

Figure 1.18 – InlineData parameters are mapped to the decorated method parameters

Figure 1.18 – InlineData parameters are mapped to the decorated method parameters

For assertion, we are using an overload of the Equal method that supports decimal precision, as illustrated in the following code snippet:

static void Equal(decimal expected, decimal actual, 
    int precision)

Run the tests, and you’ll notice that Test Explorer treats the four attributes as separate tests, as depicted in the following screenshot:

Figure 1.19 – VS Test Explorer showing grouped tests

Figure 1.19 – VS Test Explorer showing grouped tests

Even More Tests

For brevity, and given that this chapter is a limited introduction, we didn’t explore all possible testing scenarios—take, for example, int.MaxValue/int.MaxValue, int.MinValue/int.MinValue, 0/number, and 0/0.

The limits of the required tests are going to be discussed in later chapters, along with their pros and cons.

Writing tests before writing the code is not to every developer’s taste and might look unintuitive at the beginning, but you have a complete book to make you decide for yourself. In Chapter 5, Test-Driven Development Explained, you will dig deeper into implementation and best practices.

 

Summary

While this chapter is meant for a quick implementation, I trust you did have a taste of what TDD is and picked up some skills, such as xUnit, Test Explorer, test first, red/green, and a few conventions.

For a start, we have picked easy examples—of course—so, we have got no dependency injection (DI), nor mocking or anything fancy, because the stimulating stuff is coming next. So, I hope this chapter has made you excited about the rest of the book.

If you are like me when I first encountered TDD, you might be wondering the following: Why test first? Isn’t this too much unit testing code? Is unit testing effective? What is the difference between unit testing and TDD? How many tests should I write? You may have other questions too—these will be answered gradually while you progress through the book, and I promise I will make the answers as clear as possible.

In the next chapter, we will touch on a design pattern called DI, which is an essential requirement for working with TDD.

 

Further reading

To learn more about the topics discussed in the chapter, you can refer to the following links:

About the Author
  • Adam Tibi

    Adam Tibi is a London-based software consultant with over 22 years of experience in .NET, Python, the Microsoft stack, and Azure. He is experienced in mentoring teams, designing architecture, promoting agile and good software practices, and, of course, writing code. Adam has consulted for blue-chip firms including Shell, Lloyds Bank, Lloyd’s of London, Willis Towers Watson, and for a mix of start-ups. As a consultant who has a heterogeneous portfolio of clients, he has gained a solid understanding of the TDD intricacies, which he has transferred into this book.

    Browse publications by this author
Pragmatic Test-Driven Development in C# and .NET
Unlock this book and the full library FREE for 7 days
Start now