Basic unit testing with xUnit
Photo by Oscar Nord on Unsplash

Basic unit testing with xUnit

Unit testing has many benefits and will help you identify bugs and problem areas in your code. If we look at the benefits of unit testing they are as follows:

  • Bugs are found easily and a lot quicker - With unit test implementation, it will reduce the possibility of breaking changes being implemented unknowingly as if a unit test that was previously passing now fails, a breaking change has been added and needs to be addressed.
  • Saves time and money - Less time is spent trying to debug code to find the issue and issues can be caught before it reaches testing which saves a lot of time and money.
  • Helps gauge code performance - Unit tests show how long they take to execute, this can help you gauge how well your code is performing and can help identify problem areas that may need to be visited or cleaned up.
  • Help find edge cases - Unit tests are a great way to find out how your project will function with odd inputs. It can help you discover that an HTTP request fails if you pass a special character in the body or that you do not handle null correctly in a method that can accept null values.

In this article, I will be adding a new xUnit test project to my solution and giving some information on the basics of unit testing with xUnit to get you started. There will be a follow-up article on how this can be applied directly to a project to test the code within the project. This article is not a follow-on from my previous article however references are made below to the project structure I created in my previous article.

First, we need to add a new project to the solution. The new project will be a "xUnit Test Project", so right-click your solution, hover "Add" and click "New Project...". Search for a template using the following text: "XUnit". Select "xUnit Test Project" making sure it is for C# and not VB and click "Next".

No alt text provided for this image

Name the Project in accordance with what it will be testing. In this case, I will be naming it "V3.Practice.Tests" and the reason why I like to name it specifically is so that I can easily identify the test project for each function/project and know which function/project the test applies to. Click "Next". Select Target Framework as ".NET Core 3.1 (Long-term support)" to match the function we are going to be testing. Click "Create" and wait for the project to be added and compiled.

Now that the test project has been added, you will see the following code in the file "UnitTest1.cs":

using System
using Xunit;


namespace V3.Practice.Tests
{
? ? public class UnitTest1
? ? {
? ? ? ? [Fact]
? ? ? ? public void Test1()
? ? ? ? {


? ? ? ? }
? ? }
};        

Before we start writing code, we will need an understanding of the "[Fact]" attribute and explore other attributes.

  • [Fact] - This attribute is used by the test runner to identify a test method that takes no arguments or parameters.
  • [Theory] - This attribute is used by the test runner to identify a test method that takes a parameter. Once you have added this attribute, you can use the following attributes to pass in data to the method: [InlineData], [ClassData], and [PropertyData]. [InlineData] allows us to specify a specific variable that is passed into the test when it runs and is what we will be using today.

We are going to rename "Test1" to be "Fact_BasicMath_Equal" and then add a new Fact test and call it "Fact_BasicMath_NotEqual". Your code will look as follows:

using System
using Xunit;


namespace V3.Practice.Tests
{
? ? public class UnitTest1
? ? {
? ? ? ? [Fact]
? ? ? ? public void Fact_BasicMath_Equal()
? ? ? ? {


? ? ? ? }


? ? ? ? [Fact]
? ? ? ? public void Fact_BasicMath_NotEqual()
? ? ? ? {


? ? ? ? }
? ? }
};        

There are three steps I like to follow with my unit tests and they are as follows:

  1. Arrange - In this step, we prepare the variables for the test.
  2. Act - In this step, we execute the test using the variables from the first step.
  3. Assert - In this step, we check the result of the second step to see if it passes or fails.

Using what is mentioned above, we will make our class look as follows:

using System;
using Xunit;


namespace V3.Practice.Tests
{
? ? public class UnitTest1
? ? {
? ? ? ? [Fact]
? ? ? ? public void Fact_BasicMath_Equal()
? ? ? ? {
? ? ? ? ? ? // Arrange
? ? ? ? ? ? int a = 2;
? ? ? ? ? ? int b = 2;
? ? ? ? ? ? int expected = 4;


? ? ? ? ? ? // Act
? ? ? ? ? ? var response = Add(a, b);


? ? ? ? ? ? // Assert
? ? ? ? ? ? Assert.Equal(expected, response);
? ? ? ? }


? ? ? ? [Fact]
? ? ? ? public void Fact_BasicMath_NotEqual()
? ? ? ? {
? ? ? ? ? ? // Arrange
? ? ? ? ? ? int a = 2;
? ? ? ? ? ? int b = 3;
? ? ? ? ? ? int expected = 4;


? ? ? ? ? ? // Act
? ? ? ? ? ? var response = Add(a, b);


? ? ? ? ? ? // Assert
? ? ? ? ? ? Assert.NotEqual(expected, response);
? ? ? ? }


? ? ? ? private int Add(int a, int b)
? ? ? ? {
? ? ? ? ? ? return a + b;
? ? ? ? }
? ? }
}        

Now, we need to add a new view so we can see our tests. Click on "View" in the top left and select "Test Explorer".

No alt text provided for this image

Once the test explorer is visible, click the button indicated and select "Dock" from the menu and you will see the following:

No alt text provided for this image
No alt text provided for this image

Now, we can see that the two tests we created are showing and we can now run them. Click the following button to run all tests in view:

No alt text provided for this image

This will run our tests and show us the result of each test indicated with a green icon or a red icon. Your tests should look as follows:

No alt text provided for this image

If we are to change a test to fail, it will look as follows:

No alt text provided for this image

As you can see, a test failed and we are given an Error Message which can help us diagnose the issue and resolve it.

We will now perform some tests using [Theory] and [InlineData], this allows us to have multiple tests against a single method which can greatly increase the protection of code quality.

Add the following to your class:

        [Theory]
? ? ? ? [InlineData(2)]
? ? ? ? [InlineData(4)]
? ? ? ? [InlineData(5)]
? ? ? ? public void Theory_BasicMath_IsEvenNumber(int number)
? ? ? ? {
? ? ? ? ? ? // Arrange
? ? ? ? ? ? int numberToCheck = number;


? ? ? ? ? ? // Act
? ? ? ? ? ? bool IsEqualNumber = CheckEven(numberToCheck);


? ? ? ? ? ? // Assert
? ? ? ? ? ? Assert.True(IsEqualNumber);
? ? ? ? }


? ? ? ? [Theory]
? ? ? ? [InlineData(1)]
? ? ? ? [InlineData(3)]
? ? ? ? [InlineData(4)]
? ? ? ? public void Theory_BasicMath_IsOddNumber(int number)
? ? ? ? {
? ? ? ? ? ? // Arrange
? ? ? ? ? ? int numberToCheck = number;


? ? ? ? ? ? // Act
? ? ? ? ? ? bool IsEqualNumber = CheckEven(numberToCheck);


? ? ? ? ? ? // Assert
? ? ? ? ? ? Assert.False(IsEqualNumber);
? ? ? ? }


? ? ? ? private bool CheckEven(int a)
? ? ? ? {
? ? ? ? ? ? return a % 2 == 0;
? ? ? ? }        

A breakdown of what you see happening above:

  • [InlineData(2)] - This means that when the test runs, this is the parameter that is passed into the method when it is run. For each [InlineData] under a single [Theory], it will run a new test and be treated as a separate test for a single test method.
  • "CheckEven(int a)" is a method we are going to use to ascertain if a number is even or odd.

In the "Test Explorer" we can now see that we have new tests that we have not run yet:

No alt text provided for this image

And if you expand those tests, you will see each [InlineData] attribute is treated as a separate test against the single test method. We can also see each parameter that will be passed in for each test as we defined above using our [InlineData] attribute.

No alt text provided for this image

If we "Run All Tests In View" again, we will see the following:

No alt text provided for this image

Here we can see that two tests failed, the number 5 is not an even number and the number 4 is not an odd number. If we replace the 5 with a 6 and the 4 with a 5 and run it again, we will see the following:

No alt text provided for this image

All our tests have now passed!

This brings us to the end of the article, I hope that you have learned something new and/or enjoyed this article! All feedback is extremely welcome! The next article will look to covering xUnit in depth while using Moq to assist with the unit tests. Until next time!

要查看或添加评论,请登录

Declan Taggart的更多文章

  • Bicep - Parameter Decorators

    Bicep - Parameter Decorators

    In my previous article, I covered Parameters and Variables and how to use them. This article is a continuation of my…

  • Bicep - Parameters and Variables

    Bicep - Parameters and Variables

    In my previous article, I covered the creation of a basic Bicep file with the commands that are required and a…

  • Create your first Bicep file

    Create your first Bicep file

    What is Bicep? Bicep is a domain-specific language (DSL) that uses a declarative syntax to deploy Azure Resources. In a…

  • Dependency injection in Azure Function v3

    Dependency injection in Azure Function v3

    In this article, I am going to show how DI (Dependency Injection) can easily be achieved within an Azure Function…

    2 条评论
  • My basic wireframe for Azure Function v3

    My basic wireframe for Azure Function v3

    In this article, I will show you the wireframe I use for every Azure Function that I create and use. This demo will be…

    2 条评论

社区洞察

其他会员也浏览了