DEV Community

Rafael Ahrons
Rafael Ahrons

Posted on • Originally published at luturol.github.io on

Testing with XUnit and Moq

Testing with XUnit

In this article we will learn how to test your Web API with XUnit.

What is XUnit

Is an open source tool for testing C# applications.

How to configure

  1. First you need to initilize your project:
   dotnet new webapi -n MoqXUnit
Enter fullscreen mode Exit fullscreen mode
  1. Then you need to initialize a XUnit project for testing
 dotnet new xunit -n MoqXUnit.Test

Enter fullscreen mode Exit fullscreen mode

This command will create a Test Project on your root folder.

  1. Now you need to add MoqXUnit project reference to MoqXUnit.Test
 dotnet add MoqXUnit.Test reference MoqXUnit

Enter fullscreen mode Exit fullscreen mode

Having the reference you can access classes from the other project. In another words, now you can test them.

Now we conclude the configuration part. Let’s make some tests.

Testing

Let’s add some features to ours MoqXUnit Web API.

Create another controller calling CalculatorController inside MoqXUnit/Controller folder and add this code.

using Microsoft.AspNetCore.Mvc;

namespace MoqXunit.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class CalculatorController : ControllerBase
    {
        [HttpGet]
        public ActionResult SumTwoNumbers([FromQuery] int num1, 
                                          [FromQuery] int num2)
        {            
            return Ok(num1 + num2);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

SumTwoNumbers endpoint will only sum two numbers. Now let’s add some tests.

Inside MoqXunit.Test add a .cs file and add a code more like:

using System;
using Microsoft.AspNetCore.Mvc;
using MoqXunit.Controllers;
using Xunit;

namespace MoqXunit.Teste
{
    public class CalculatorControllerTest
    {
        [Theory]
        [InlineData(1, 2)]
        [InlineData(1, 5)]
        [InlineData(1, 6)]
        [InlineData(10, 2)]
        [InlineData(1999, 2)]
        public void Sum_Two_Numbers_using_theory(int num1, int num2)
        {
            //arrange
            CalculatorController controller = new CalculatorController();
            int expected = num1 + num2;

            //act
            var response = controller.SumTwoNumbers(num1, num2).Result as OkObjectResult;

            //assert
            Assert.NotNull(response);
            Assert.Equal(response.Value, expected);
        }

        [Fact]
        public void Sum_two_numbers_using_fact()
        {
            //arrange
            CalculatorController controller = new CalculatorController();
            int num1 = 1;
            int num2 = 2;
            int expected = num1 + num2;            

            //act
            var response = controller.SumTwoNumbers(num1, num2).Result as OkObjectResult;

            //assert
            Assert.NotNull(response);
            Assert.Equal(response.Value, expected);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

XUnit has two types of anottations to say that this method is a Test Method: Fact and Theroy.

  • Fact for methods with invariant data.
  • Theory with variant data and those data is set by using InlineData anottations.

Using Asset Methods you can validade the results from your test. Go check the available ones.

Using Moq

Moq is a library to Mock some interfaces to make testing easier.

How to add to your test project

you can just execute the following command:

dotnet add .\MoqXunit.Teste\ package Moq

Enter fullscreen mode Exit fullscreen mode

Change .\MoqXunit.Teste\ to the name of your project.

Now you have it installed.

How to use Moq

You gonna use to mock some integrations that you don’t want to execute in your test, like, databases.

Let’s create a folder inside MoqXunit project called Interfaces and add a interface for a ICalculatorRepository.cs We are not implementing calculator repository, it’s just an example.

using System.Collections.Generic;
using MoqXunit.Models;

namespace MoqXunit.Interfaces 
{
    public interface ICalculatorRepository
    {
        IEnumerable<Calculation> GetAllCalculations();
        void SaveCalculation(Calculation calc);
    }
}

Enter fullscreen mode Exit fullscreen mode

And let’s create this Calculation class:

namespace MoqXunit.Models
{
    public record Calculation
    {
        public int Value1 { get; init; }
        public int Value2 { get; init; }
        public int Result { get; init; }

    }
}

Enter fullscreen mode Exit fullscreen mode

Now let’s make some changes in our code to use this interface and calculation class.

using System.Linq;
using Microsoft.AspNetCore.Mvc;
using MoqXunit.Interfaces;
using MoqXunit.Models;

namespace MoqXunit.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class CalculatorController : ControllerBase
    {
        private ICalculatorRepository _repository;

        public CalculatorController(ICalculatorRepository repository)
        {
            _repository = repository;
        }

        [HttpGet]
        public ActionResult SumTwoNumbers([FromQuery] int num1,
                                          [FromQuery] int num2)
        {

            var allCalculations = _repository.GetAllCalculations();
            if (allCalculations.Any(e => e.Value1 == num1 && e.Value2 == num2))
            {
                return Ok(allCalculations.First(e => e.Value1 == num1 && e.Value2 == num2));
            }
            else
            {
                var calculation = new Calculation
                {
                    Value1 = num1,
                    Value2 = num2,
                    Result = num1 + num2
                };

                _repository.SaveCalculation(calculation);

                return Ok(calculation);
            }
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

We add ICalculatorRepository as a dependency to our controller, and add a few changes on SumTwoNumbers to use GetAllCalculations and SaveCalculation method. Now we have this endpoint that if calculation exist in the database, it will return the result from it. Else it will create a instance of Calculation and save it with the result. The two paths will return Calculation object as response.

To test this controller we need to make some changes and configurate the responses from GetAllCalculation and SaveCalculation methods. We do not want to execute the implementation of this interface.

Moq solve this issue by using a class called Mock. With Mock you can create a setup to the interface by setting it’s methods income and output when it will be called inside the method that we are testing.

[Fact]
        public void Sum_two_numbers_using_fact()
        {
            //arrange
            var mock = new Mock<ICalculatorRepository>();

            var controller = new CalculatorController(mock.Object);

            //... all the other operations above
        }

Enter fullscreen mode Exit fullscreen mode

Instantiate a Mock object by setting ICalculatorRepository as the type and pass mock.Object as parameter to CalculatorController and now you have mocked ICalculatorRepository.

Inside SumTwoNumber method from CalculatorController we have a call to GetAllCalculations that will return all calculation in a IEnumerable. How to set that? By calling Setup and Return in mock instance.

Example:

mock.Setup(e => e.GetAllCalculations()).Returns(Enumerable.Empty<Calculation>());

Enter fullscreen mode Exit fullscreen mode

Our full method test will be something like this:

        [Fact]
        public void Sum_two_numbers_using_fact()
        {
            //arrange
            var mock = new Mock<ICalculatorRepository>();
            mock.Setup(e => e.GetAllCalculations()).Returns(Enumerable.Empty<Calculation>());
            mock.Setup(e => e.SaveCalculation(It.IsAny<Calculation>()));
            var controller = new CalculatorController(mock.Object);

            int num1 = 1;
            int num2 = 2;
            int expected = num1 + num2;            

            //act
            var response = controller.SumTwoNumbers(num1, num2) as OkObjectResult;

            //assert
            Assert.NotNull(response);
            Assert.Equal(response.Value, expected);
        }

Enter fullscreen mode Exit fullscreen mode

You can also mock a method from a class that does not have a interface by setting virtual in the method declaration.

Discussion (0)