In the previous article, we learned how to write Unit Tests by using the xUnit and the different attributes that xUnit provides for us. We’ve also seen how to test validation rules inside a single validation class.
But what about controllers and all the actions inside? Can we write tests for them too?
In this article, we are going to talk more about testing controllers in ASP.NET Core applications by using Moq and unit tests.
For the complete navigation of this series, you can visit ASP.NET Core Testing.
So, let’s get going.
Unit Testing Controllers Using Moq Library
Before we start, let’s take a look at the EmployeesController’s constructor code:
As you can see, we are using Dependency Injection to inject the interface into our controller. So basically, our controller has a dependency on the repository logic through that injected interface.
And there is no problem with that approach at all, it is even recommended. But when we write tests for our controller or any other class in a project, we should isolate those dependencies.
There are several advantages to isolating dependencies in a test code:
- We don’t have to initialize all dependencies to return correct values, thus making our test code much simplified
- If our test fails and we didn’t isolate dependency, we can’t be sure whether it fails due to some error in a controller or in that dependency
- When dependent code communicates with a real database, as our repository does, the test code could take more time to execute. This can happen due to connection issues or simply due to the time needed to fetch the data from the database.
Of course, there are additional reasons to isolate dependencies in test code, but you get the point.
That said, let’s install the Moq library in the EmployeesApp.Tests project:
Install-Package Moq
After the installation completes, we are going to create a new Controller folder in the same project and add EmployeesControllerTests class.
Creating a Mock Object
Let’s modify the EmployeesControllerTests class:
public class EmployeesControllerTests
{
private readonly Mock<IEmployeeRepository> _mockRepo;
private readonly EmployeesController _controller;
public EmployeesControllerTests()
{
_mockRepo = new Mock<IEmployeeRepository>();
_controller = new EmployeesController(_mockRepo.Object);
}
}
We create a mock object of type IEmployeeRepository inside the constructor, and since we want to test the controller logic, we create an instance of that controller with the mocked object as a required parameter.
And there you go. Everything is prepared, and a dependency is mocked, so all we have to do is to write some tests.
Unit Testing the Index Action
If we take a look at the Index action in the EmployeesController class, we can see that we fetch all employees from the database and return a view with those employees:
public IActionResult Index()
{
var employees = _repo.GetAll();
return View(employees);
}
As a result, we can write a couple of tests to verify that this action is doing exactly what it is supposed to do. Also, you will see that testing controllers with unit tests is not that hard at all.
In the first test, we are going to verify that the Index action returns a result of type ViewResult:
[Fact]
public void Index_ActionExecutes_ReturnsViewForIndex()
{
var result = _controller.Index();
Assert.IsType<ViewResult>(result);
}
So, we are executing the Index action from our controller and accepting the result inside the result variable. After that, we check the type of the returned result with the IsType method. If the result is of the ViewResult type the test will pass, otherwise, it will fail.
In this test method, we didn’t use a mocked object because we didn’t use any of our repository methods. We have just checked the type of our result.
Let’s run the Test Explorer window and find out the result:
We can see that our test passes and that the result is of ViewResult type.
Now, let’s continue by writing another test method to verify that our Index action returns an exact number of employees:
[Fact]
public void Index_ActionExecutes_ReturnsExactNumberOfEmployees()
{
_mockRepo.Setup(repo => repo.GetAll())
.Returns(new List<Employee>() { new Employee(), new Employee() });
var result = _controller.Index();
var viewResult = Assert.IsType<ViewResult>(result);
var employees = Assert.IsType<List<Employee>>(viewResult.Model);
Assert.Equal(2, employees.Count);
}
In this test method, we are fetching the data from the database by using the GetAll repository method. Of course, we don’t want to use the concrete repository but the mocked one and therefore we use the Setup method to specify a setup for the GetAll method. Additionally, we have to use the Returns method to specify the value to return from the mocked GetAll method.
After we store the result of the Index action, we check the type of that result, the type of the model object inside that result, and finally the number of employees by using the Equal method. All three verifications have to pass in order for the test to pass, otherwise, the test will fail.
Once we run the test, we can see it passes and we verify that the Index action returns exactly two employees.
Testing Create Actions
We have two Create actions in our EmployeesController class, the GET, and the POST action. The first action just loads the Create View and that is something we have to test.
So, let’s do it:
[Fact]
public void Create_ActionExecutes_ReturnsViewForCreate()
{
var result = _controller.Create();
Assert.IsType<ViewResult>(result);
}
We already had a test like this, just with the Index action, so, there is nothing new about it. If we run Test Explorer we can verify that the test passes.
Let’s move on to the second Create action, the POST one. In that action, we have a model validation and if it is invalid we return a view with the employee object.
So let’s test that:
[Fact]
public void Create_InvalidModelState_ReturnsView()
{
_controller.ModelState.AddModelError("Name", "Name is required");
var employee = new Employee { Age = 25, AccountNumber = "255-8547963214-41" };
var result = _controller.Create(employee);
var viewResult = Assert.IsType<ViewResult>(result);
var testEmployee = Assert.IsType<Employee>(viewResult.Model);
Assert.Equal(employee.AccountNumber, testEmployee.AccountNumber);
Assert.Equal(employee.Age, testEmployee.Age);
}
We have to add a model error to the ModelState property in order to test an invalid model state.
After that, we create a new employee without the Name property, which makes it invalid as well.
Finally, we call the create action and execute a couple of assertions.
With assert statements, we verify that the result is of type ViewResult and that the model is of the Employee type. Additionally, we are making sure that we get the same employee back by comparing property values from the testEmployee and the employee objects:
Additional Invalid Model Test
Let’s write one additional test to verify that the CreateEmployee method, from our repository, never executes if the model state is invalid:
[Fact]
public void Create_InvalidModelState_CreateEmployeeNeverExecutes()
{
_controller.ModelState.AddModelError("Name", "Name is required");
var employee = new Employee { Age = 34 };
_controller.Create(employee);
_mockRepo.Verify(x => x.CreateEmployee(It.IsAny<Employee>()), Times.Never);
}
The first three lines of code are the same as in the previous test method, we add a model error, create an invalid employee object, and call the Create action from our controller.
Of course, in that action, we have the CreateEmployee method which shouldn’t be executed if the model is invalid. That is exactly what we verify with the Verify method from the mocked object. By using the It.IsAny<Employee> expression, we state that it doesn’t matter which employee is passed as a parameter to the CreateEmployee method. The only important thing is that the parameter is of the Employee type.
The last parameter of the Verify method is the number of times our method executes. We use Times.Never because we don’t want to execute the CreateEmployee method at all if the model state is invalid.
You can run the test to see that it passes.
If we modify the test code by placing the Times.Once instead of Times.Never the test will fail for sure. The message that you get explains pretty well what is the problem and why the test fails.
Testing if Model is Valid in the Create Action
If the Model State is valid the CreateEmployee method should be executed just once:
[Fact]
public void Create_ModelStateValid_CreateEmployeeCalledOnce()
{
Employee? emp = null;
_mockRepo.Setup(r => r.CreateEmployee(It.IsAny<Employee>()))
.Callback<Employee>(x => emp = x);
var employee = new Employee
{
Name = "Test Employee",
Age = 32,
AccountNumber = "123-5435789603-21"
};
_controller.Create(employee);
_mockRepo.Verify(x => x.CreateEmployee(It.IsAny<Employee>()), Times.Once);
Assert.Equal(emp.Name, employee.Name);
Assert.Equal(emp.Age, employee.Age);
Assert.Equal(emp.AccountNumber, employee.AccountNumber);
}
So, we set up the CreateEmployee method with any employee and use the Callback method to populate the emp object with the values from the employee parameter. After that, we create a local employee object, execute the Create action with that employee and Verify if the CreateEmployee method has been executed just once.
Additionally, we verify that the emp object is the same as the employee object provided to the Create action.
Finally, let’s run that in the Test Explorer:
One more test to go:
[Fact]
public void Create_ActionExecuted_RedirectsToIndexAction()
{
var employee = new Employee
{
Name = "Test Employee",
Age = 45,
AccountNumber = "123-4356874310-43"
};
var result = _controller.Create(employee);
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
If we take a look at the Create action in our controller, we are going to see that after creating a new employee, we redirect a user to the Index action.
Well, that is exactly what we’ve successfully tested in our test method.
Conclusion
That is it. As you can see, testing controllers can be quite an easy job when you have the right tool to do that. We have used the Index and Create actions to show you how to test our controller and actions inside it, but all the rules can be applied to other types of Actions (PUT, DELETE…).
So, to sum up, we have learned:
- How to use the Moq library to isolate dependency in the test code
- To write different tests for our actions inside a controller
In the next part, we are going to talk about integration tests and how to create an in-memory database.






very helpful thanks
You are most welcome.
Hi,
Great article. How would we go about Mocking our services and testing our service and controllers in Onion architecture, such as how you setup the project in your ebook. Thanks
Hello Justin. You can take a look at the comment from Peter Rundqvist, it might help you. It is just about mocking it properly.
This is so useful! Just what I need for my job. I love this site so much. Keep up the good work! Pozz iz Hrvatske! 🙂
I’m glad you like it and it sad useful to you. Pozz iz Srbije 🙂
Great series, again. Thank you. Just two small remarks: the first screenshot with the controller contains the validation, however, in the github repo it is without. Then, the snippet with Create_ModelStateValid_CreateEmployeeCalledOnce, begins without [Fact].
Hi Zoltan. I am not sure that first statement is true. You may have looked at the wrong project (test one). The main project’s controller contains the Validation class insite the constructor. Regarding the missing [Fact] attribute, we are going to solve that.
@@disqus_GtEeIZUUDN:disqus Thanks for this article it is very useful. how ever I just new on unit test. Can you please also share how we can mock and test for RepositoryWrapper too please?
Hello Tomy. Please read entire Testing series, you have the link at the top of this article. You can find all the info yiu require including the mocking of repo classes.
Hi @@disqus_GtEeIZUUDN:disqus thanks for your reply. But I can see only Mock but not for the Mock in unit test series. I believe that IRepositoryWrapper will contain IEmployeeRepository Can you please point me to the link thanks. Or give example for unit test for Mock.
Ho Tomy. Did you find a solution for this? I’m having the same problem as you, I can’t test my controllers because they’re expecting an IRepositoryWrapper and if you mock it (using Moq) it always return an “Object reference not set to an instance of an object” wich has sense because it fails when it tries to instantiate the UserRepository (or the dbContext i’m not sure) in the RepositoryWrapper
We have some issues with comments, but Peter Rundqvist replied to your comment, and I will share it here with you:
“I got it working by instantiating the mock repo like this:
_mockRepo = new Mock() { DefaultValue = DefaultValue.Mock };
Setting “DefaultValue” to “DefaultValue.Mock” will create a recursive Mock, initializing all members.
You can read more about it here: https://github.com/Moq/moq4/wiki/Quickstart
Kind regards,
Peter”
I hope this helps.
I got it working by instantiating the mock repo like this:
_mockRepo = new Mock() { DefaultValue = DefaultValue.Mock };
Setting “DefaultValue” to “DefaultValue.Mock” will create a recursive Mock, initializing all members.
You can read more about it here: https://github.com/Moq/moq4/wiki/Quickstart
Kind regards,
Peter
will go throw the test even there is a Cookie object or Cache object in the Action?
That depends what action you test and what do you test in that action.
Another very useful library regarding tests – in my optionion – is FluentAssertions.
https://fluentassertions.com/introduction
Thank you Tho Mai. The library is looking great. Best regards.