Arrange.Act.Assert.

ag2byte

Abhigyan Gautam

Posted on June 19, 2023

Arrange.Act.Assert.

We will now write tests for the Fruit Controller. As discussed before there are three steps that make up a unit test - Arrange, Act, Assert. We will go through each of these steps in this article. First create an Empty class in RestTestUNIT project, let's call it FruitControllerTest.cs. Add the following to it.

namespace RestTestUNIT;
using RestTest.Service;
using RestTest.Controllers;
public class FruitControllerTest
{
    private FruitBAsketController fruitbasketcontroller;

    [SetUp]
    public void Setup()
    {

    }

    [Test]
    public void GetFruitsTest()
    { 
        Assert.Pass();
    }
}

Enter fullscreen mode Exit fullscreen mode

In .NET, we can associate functions inside the class with different testing tags. Some of these are:

  • OneTimeSetup - to define logic that run only once while running the Tests.
  • Setup - to definde code that must be executed before each testcase
  • Test - marks a function as test function
  • TestCase() - used to pass parameters to the test functions

Arrange

We need to provide the input parameters and the expected result in this step. We have a very simple api with just static data instead of data from a database, but in real applications this is not the case. For testing there, it is not viable to make database requests for each test. Thus there we use something called as Mock Response. We will try to use the same approach here as well. To mock data, we will use a package called Moq. Go ahead and install Moq from Nuget manager.

installing moq

Lets create some responses now. We will test the following cases:

  • When no argument is passed : this should return a list of fruits with no filter
  • When fruitname is passed: this should only return a list of fruits with this name
  • When fruitname and sorted is passed: this should return fruit list with the same name and state of sorted
  • When fruitname is not present : this should return a BadRequest response.

Add the following objects to the FruitControllerTest class

    private string allfruits = @"
[
  {
    'name': 'Apple',
    'price': 43,
    'sorted': false
  },
  {
    'name': 'Litchi',
    'price': 32,
    'sorted': false
  },
  {
    'name': 'Litchi',
    'price': 32,
    'sorted': true
  },
  {
    'name': 'Mango',
    'price': 32,
    'sorted': false
  },
  {
    'name': 'Pineapple',
    'price': 26,
    'sorted': false
  },
  {
    'name': 'Apple',
    'price': 20,
    'sorted': false
  }
]";
    private string onlyapple = @"[
  {
    'name': 'Apple',
    'price': 43,
    'sorted': false
  },
  {
    'name': 'Apple',
    'price': 20,
    'sorted': false
  },
  {
    'name': 'Apple',
    'price': 32,
    'sorted': true
  }
]";
    private string onlytrueapple = @"[
  {
    'name': 'Apple',
    'price': 32,
    'sorted': true
  }
]";
private fruitList = @"";
Enter fullscreen mode Exit fullscreen mode

We will use these as the mocked responses. Since the data is returned from GetFruits() in the FruitService class, We will mock the response for this Service. For that, we will use the interface IFruitService as interface only contains the declaration of the function and not its implementation.

In FruitBasketController add another constructor that initialises the controller with the interface instead of the actual service

    public FruitBAsketController(Object fruitServiceObject)
    {
        fruitservice = (IFruitService)fruitServiceObject;
    }
Enter fullscreen mode Exit fullscreen mode

We will use this constructor for our mocked response. In the Setup() we will setup our controller for testing. Add the following in the Setup()

    private Mock<IFruitService> fruitmock;
    [SetUp]
    public void Setup()
    {
        fruitmock = new Mock<IFruitService>();
        fruitbasketcontroller = new FruitBAsketController(fruitmock.Object);
    }
Enter fullscreen mode Exit fullscreen mode

Here we are setting up such that the fruitbasketcontroller will now refer the fruitmock object now when calling the service, instead of the actual definition.

Act

In this step, we will call our functions to return functions to return the above responses based on different test cases. Add the following function in FruitControllerTest.cs

    [Test]
    [TestCase("",false)]
    [TestCase("Apple", false)]
    [TestCase("Apple", true)]
    [TestCase("Kiwi", false)]

    public void GetFruitsTest(string fruitname="",bool sorted = false)
    {
        List<Fruit>? a = null;
        if(!sorted)
        {
            if (fruitname.Length == 0) // get all fruits
                fruitList = allfruits;
            else if (fruitname.Length > 0)
                if (fruitname == "Apple" || fruitname == "apple") // get only apples
                fruitList = onlyapple;

        }
        else // sorted is true
        {
            if (fruitname.Length > 0)
                if (fruitname == "Apple" || fruitname == "apple") // only true apple
                    fruitList = onlytrueapple;
                //if the fruit does not exist fruitList is empty

        }

        if(fruitList.Length > 0)
            a = JsonConvert.DeserializeObject<List<Fruit>>(fruitList);
        fruitmock.Setup(x => x.GetFruits("", false)).Returns(a);
        var res = fruitbasketcontroller.GetAllFruits("", false);
Enter fullscreen mode Exit fullscreen mode

The different values for parameters are passed through the TestCase tag. Using this parameters, we populate the list fruitList which is then converted into List<Fruit> using JsonConvert.

Since we are using a mock object for FruitService, we can set it up to return response based on the above steps. This means it does not go into actual definition of functions in FruitService class but just mocks the response.

Assert

In the third stage, we will assert the responses ,i.e, we will validate the response returned from the function. Add the following Assertions just below the above code in GetFruitsTest().

       var res = fruitbasketcontroller.GetAllFruits(fruitname, sorted);
        if(((IStatusCodeActionResult)res).StatusCode == 200)
        {
            var okObject = res as OkObjectResult;
            var fruitList = okObject.Value as List<Fruit>;
            foreach(Fruit obj in fruitList)
            {
                Assert.IsNotNull(obj.name);
                if (fruitname.Length > 0)
                    Assert.True(fruitname.Equals(obj.name, StringComparison.OrdinalIgnoreCase));
                Assert.IsNotNull(obj.price);
                Assert.IsNotNull(obj.sorted);
                if (sorted)
                    Assert.True(obj.sorted == sorted);

            }

        }
        else if(((IStatusCodeActionResult)res).StatusCode == 400)
        {

            var badRequest = (res as BadRequestObjectResult).Value;
            Assert.True(badRequest == "Found Nothing");

        }
Enter fullscreen mode Exit fullscreen mode

Since the Service can return two types of responses with status code 200 and 400, we will make our assertions accordingly. And with that we have successfully written Unit Test for out controller. Go ahead and run these tests from Test Explorer.

lets go

Thanks for reading!

💖 💪 🙅 🚩
ag2byte
Abhigyan Gautam

Posted on June 19, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related