Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Pragmatic Test-Driven Development in C# and .NET
Pragmatic Test-Driven Development in C# and .NET

Pragmatic Test-Driven Development in C# and .NET: Write loosely coupled, documented, and high-quality code with DDD using familiar tools and libraries

By Adam Tibi
$33.99 $22.99
Book Sep 2022 372 pages 1st Edition
eBook
$33.99 $22.99
Print
$41.99
Subscription
$15.99 Monthly
eBook
$33.99 $22.99
Print
$41.99
Subscription
$15.99 Monthly

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Sep 30, 2022
Length 372 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781803230191
Category :
Table of content icon View table of contents Preview book icon Preview Book

Pragmatic Test-Driven Development in C# and .NET

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:

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Build a full TDD-based app employing familiar tools and libraries to practice real-world scenarios
  • Derive your architecture using TDD with domain-driven design and SOLID approach
  • Know the challenges of rolling out TDD and unit testing into your organization and build a plan

Description

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.

What you will learn

Writing unit tests with xUnit and getting to grips with dependency injection Implementing test doubles and mocking with NSubstitute Using the TDD style for unit testing in conjunction with DDD and best practices Mixing TDD with the ASP.NET API, Entity Framework, and databases Moving to the next level by exploring continuous integration with GitHub Getting introduced to advanced mocking scenarios Championing your team and company for introducing TDD and unit testing

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Sep 30, 2022
Length 372 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781803230191
Category :

Table of Contents

21 Chapters
Preface Chevron down icon Chevron up icon
Part 1: Getting Started and the Basics of TDD Chevron down icon Chevron up icon
Chapter 1: Writing Your First TDD Implementation Chevron down icon Chevron up icon
Chapter 2: Understanding Dependency Injection by Example Chevron down icon Chevron up icon
Chapter 3: Getting Started with Unit Testing Chevron down icon Chevron up icon
Chapter 4: Real Unit Testing with Test Doubles Chevron down icon Chevron up icon
Chapter 5: Test-Driven Development Explained Chevron down icon Chevron up icon
Chapter 6: The FIRSTHAND Guidelines of TDD Chevron down icon Chevron up icon
Part 2: Building an Application with TDD Chevron down icon Chevron up icon
Chapter 7: A Pragmatic View of Domain-Driven Design Chevron down icon Chevron up icon
Chapter 8: Designing an Appointment Booking App Chevron down icon Chevron up icon
Chapter 9: Building an Appointment Booking App with Entity Framework and Relational DB Chevron down icon Chevron up icon
Chapter 10: Building an App with Repositories and Document DB Chevron down icon Chevron up icon
Part 3: Applying TDD to Your Projects Chevron down icon Chevron up icon
Chapter 11: Implementing Continuous Integration with GitHub Actions Chevron down icon Chevron up icon
Chapter 12: Dealing with Brownfield Projects Chevron down icon Chevron up icon
Chapter 13: The Intricacies of Rolling Out TDD Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon
Appendix 1: Commonly Used Libraries with Unit Tests Chevron down icon Chevron up icon
Appendix 2: Advanced Mocking Scenarios Chevron down icon Chevron up icon

Customer reviews

Filter icon Filter
Top Reviews
Rating distribution
Empty star icon Empty star icon Empty star icon Empty star icon Empty star icon 0
(0 Ratings)
5 star 0%
4 star 0%
3 star 0%
2 star 0%
1 star 0%

Filter reviews by


No reviews found
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.