In the previous article, we have learned how to write Unit Tests by using the xUnit and different attributes that xUnit provides for us. We have 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?

Sure we do.

In this article, we are going to explain how to do that.

You can download the source code on our GitHub repository.

For the complete navigation of this series, you can visit ASP.NET Core MVC Testing.

These are the topics we are going to cover:

So, let’s get going.

Using Moq Library to Create Mock Objects While Testing MVC Controllers

Before we start, let’s take a look at the EmployeesController’s constructor code:

DI - Testing MVC Controllers

As you can see, we are using Dependency Injection to inject the interface in our controller. So basically, our controller has a dependency towards 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 from 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 due to connection issues or simply the process of data fetching is taking its

Of course, there are additional reasons to isolate dependencies in test code, but you get the point.

That being said, let’s install the Moq library in the EmployeesApp.Tests project:

Moq library - Testing MVC Controllers

After the installation completes, we are going to create a new Controller folder in the same project and add EmployeesControllerTests class:

Controller Structure

Creating a Mock Object

Let’s modify the EmployeesControllerTests class:

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, a dependency is mocked, so all we have to do is to write some tests.

Testing 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:

As a result, we can write a couple of tests to verify that this action is doing exactly what it is supposed to do.

In the first test, we are going to verify that the Index action returns a result of type ViewResult:

Tip: If Visual Studio reports errors with the _controller, Index() and ViewResult, you’ll have to install additional libraries in the test project. Just use the Quick Actions menu (CTRL+.) and accept all libraries and assemblies that VS wants to install:

Required libraries / assemblies - Testing MVC Controllers

So, we are executing the Index action from our controller and accept 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 type ViewResult the test will pass, otherwise, it will fail.

Let’s run the Test Explorer window and find out the result:

Index of type ViewResult test passes

We can see that our test passes and that the result is of ViewResult type.

Let’s write another test method to verify that our Index action returns an exact number of employees:

In the previous 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.

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 to pass the test, otherwise, the test will fail.

Let’s see this test in action:

Index returns exact number of employees

The test indeed passes and we have verified that it 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:

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 test passes:

Create action returns view - Testing MVC Controllers

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:

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:

Create action Invalid Model State

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:

The first three code lines 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. In this situation we use Times.Never because we don’t want the CreateEmployee method to execute at all if the model is invalid.

Back to Test Explorer:

Create action Invalid Model State CreateEmployee never executes

As you can see, the test passes as expected.

If we modify the test code by placing the Times.Once instead of Times.Never the test will fail for sure. You might want to try it yourself:

Create action Invalid Model State CreateEmployee never executes test fail

The message 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:

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:

Create action Valid Model State CreateEmployee called once

One more test to go:

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. 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 on 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.

If you have enjoyed reading this article and if you would like to receive the notifications about the freshly published .NET Core content we encourage you to subscribe to our blog.