Arrange.Act.Assert.
Tags: dotnet, unittest, testing
Published: 2023-06-19 11:59:30

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.

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

    [SetUp]
    public void Setup()
    {

    }

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

```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zzz2v0sdgkltor86741.jpg)

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
```cs
    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 = @"";
```

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
```cs
    public FruitBAsketController(Object fruitServiceObject)
    {
        fruitservice = (IFruitService)fruitServiceObject;
    }
```
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()`
```cs
    private Mock<IFruitService> fruitmock;
    [SetUp]
    public void Setup()
    {
        fruitmock = new Mock<IFruitService>();
        fruitbasketcontroller = new FruitBAsketController(fruitmock.Object);
    }
```
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` 
```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);
```
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()`.
```cs
       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");

        }
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qarqzfnn2oboqer105o.gif)


Thanks for reading!