Up to this point we have spent a lot of time building up the infrastructure and setting up our project as an MVC application. Along the way we have talked a lot about popular MVC patterns such as DI (Dependency Injection) and the Repository pattern.
After all of this work we have only just created our first feature; displaying a list of vehicles to the user. But once all of this initial work is done, features we build going forward should snap more quickly into place.
One of the biggest benefits of an MVC application is it’s testability. In this module we are going to create our first unit test. We need to test that the controller can access a list of vehicles through the repository.
Create the Unit Test project
The first thing we need to do is create the unit test project, add it to the solution, and make a reference from the unit test project to the main FredsCars project.
Create the xUnit project
In this chapter we are going to use the xUnit testing framework.
xUnit is a popular unit testing framework for .NET applications. xUnit provides a simple way to write and execute unit tests. xUnit is widely used in the .NET ecosystem and is the preferred testing framework for modern .NET applications.
What are unit tests?
Unit tests in ASP.NET Core MVC are automated tests that validate the functionality of individual components (such as controllers, services, and repository classes) in isolation. The goal of unit testing is to ensure that each unit of the application works as expected independently, without relying on external dependencies like databases, file systems, or APIs.
Other Testing Frameworks.
There are several testing frameworks available to use. Throughout the years I have used MSTest and NUnit. But xUnit is now the preferred unit test framework for .Net Core.
Open up a command window and use the dotnet new
command to create an xUnit project in the FresCars solution folder; the folder that contains the FredsCars.sln file.
dotnet new xunit -n FredsCars.Tests --framework net9.0
Once the command finishes running, there should be a FredsCars.Tests
folder next to the FredsCars project and the solution file in the solution folder.

Add the Test Project to the Solution
Run the command below in the solution folder.
dotnet sln FredsCars.sln add FredsCars.Tests
Once the command finishes the output will show that the test project has been added to the solution.

In Visual Studio there will be a popup message saying that the solution file has been modified outside of the environment. Click the Reload button.

Now the FredsCars.Tests project should show up in Solution Explorer.

Add a reference to the main project
Run the command below in the solution folder.
dotnet add FredsCars.Tests reference FredsCars
Once the command finishes, the output will show that a reference to the FredCars project has been added to the test project.

You can see the reference was added in Solution Explorer by expanding the Dependencies folder in the FredsCars.Tests project and from there expanding Projects.

Delete the default unit test
When you create an xUnit project, there is a default test file called UnitTest1.cs. You can delete that now.
Add the Moq package to the Test project
The unit test we are going to write is for the Home Controller. But as stated earlier, we want to make sure any error we get is actually from the controller itself and not from the repository service. The repository service is said to be a dependency of the Home Controller because it is declared as an incoming parameter of the Home Controller’s constructor method.
public HomeController(IVehicleRepository repo)
{
_repo = repo;
}
In order to make sure the repo (or the dependency) is not the cause of any potential error, we need to be able to control it’s behavior, in this case the data (or test data) that it returns. So, we need to fake, or mock, the repository.
We are going to use the Moq
package to mock the vehicle repository so the next thing we need to do is install the package. In a command window run the following command in the FredsCars.Tests project folder; the folder with the FredsCars.Tests.csproj file.
dotnet add package Moq --version 4.20.72
NOTE: version 4.20.72 is the latest version at the time of this writing. Remember to always check back in Nuget.org for the latest version.
Once the command finishes running, you can verify the package was installed in Solution Explorer by expanding the Packages node.

Write the Unit Test: First Draft
A lot of books and tutorials I read put all of the unit tests on the root of the test project. To me it seems you would end up with a huge number of disorganized tests you have to dig through to find what test you are looking for when you need it. I like to try to organize my tests with the same folder structure of the project I am testing. So, create a folder called Controllers in the root of the test project. Then add a class file in the Controllers folder called HomeControllerTests_Fails.cs.
I want to take you through the process of writing a unit test for the Index method of the Home Controller. But because the Index method is asynchronous, there are a few extra steps we need to perform to get the test to succeed.
First we are going to write a test that fails, inspect the error, and then apply the fixes.
So, in the new Controllers folder create a file called HomeControllerTests_Fails.

Modify the HomeControllerTests_Fails.cs file with the code below.
FredsCars.Tests\Controllers\HomeControllerTests_Fails.cs
using FredsCars.Controllers;
using FredsCars.Models;
using FredsCars.Models.Repositories;
using Moq;
namespace FredsCars.Tests.Controllers
{
public class HomeControllerTests_Fails
{
[Fact]
public async Task Can_Access_VehicleList_FromVehicleRepo()
{
// Arrange
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
mockVehicleRepo.Setup(mvr => mvr.Vehicles).Returns((new Vehicle[]
{
new Vehicle
{
Id = 1,
Make = "Make1",
Model = "Model1",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
},
},
new Vehicle
{
Id = 2,
Make = "Make2",
Model = "Model2",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
},
},
new Vehicle
{
Id = 3,
Make = "Make3",
Model = "Model3",
VehicleType = new VehicleType
{
Id = 2,
Name = "Truck"
},
},
new Vehicle
{
Id = 4,
Make = "Make4",
Model = "Model4",
VehicleType = new VehicleType
{
Id = 3,
Name = "Jeep"
},
}
}
).AsQueryable<Vehicle>());
HomeController controller = new HomeController(mockVehicleRepo.Object);
// Act
IEnumerable<Vehicle>? result =
(await controller.Index()).ViewData.Model
as IEnumerable<Vehicle>;
// Assert
Vehicle[] vehicleArray = result?.ToArray()
?? Array.Empty<Vehicle>();
Assert.True(vehicleArray.Length == 4);
int carCount = vehicleArray.Where(v => v.VehicleType.Name == "Car").Count();
Assert.Equal(2, carCount);
Assert.True(vehicleArray[3].Make == "Make3" &&
vehicleArray[3].Model == "Model3");
Assert.True(vehicleArray[4].Make == "Make4" &&
vehicleArray[3].Model == "Model4");
}
}
}
Run the Unit Test: First Draft
Test Explorer
To run the unit test we can use Visual Studio’s built in Test Explorer.
Open the Test Explorer by selecting Test -> Test Explorer
from the top menu in Visual Studio. The window will open in the right panel of Visual Studio in a tab next to Solution Explorer.

As you can see it discovers the first test called Can_Access_VehicleList_FromVehicleRepo
under the HomeControllerTests_Fails.cs
node representing the class the unit test is written in.
When a test fails the error messages can be quite lengthy so feel free to undock the Test Explorer to make it bigger.

To run the test right click on the Can_Access_VehicleList_FromVehicleRepo
test node in Test Explorer and select the Run option in the context menu with the green arrow icon.

When a test fails it will show a red circle with an x in the center to its left. When it passes there will be a green circle with a checkmark in its center. Here we can see that the test failed.

In the Test Detail Summary pane of the Test Explorer it shows there was a System.InvalidCastException
.
The error reads,
"Unable to cast object of type 'System.Linq.EnumerableQuery1[FredsCars.Models.Vehicle]'
to type
'System.Collections.Generic.IAsyncEnumerable
1[FredsCars.Models.Vehicle]'.
We also see in the stack trace that the error happens at line 19 in the Index method of the Home Controller which was called by our unit test at line 68.

What the error message is saying is that the Index method is trying to convert a type of EnumerableQuery
from the System.Linq
namespace to a type of IAsyncEnumerable
in the System.Collections.Generic
namespace.
This is a hard error to track down because the method runs fine without error at run time and there are no errors (red squiggles) in the HomeControllerTests_Fails.cs
file at compile time or while we are writing the code.
The error happens in the Index method of the controller when we try to call the Vehicles property in the repository asynchronously using the ToListAsync
method.
public async Task<ViewResult> Index()
{
return View(await _repo.Vehicles
.Include(v => v.VehicleType)
.ToListAsync());
}
This runs fine at run time when the IVehicleRepository
is implemented with the EFVehicleRepository
.
public IQueryable<Vehicle> Vehicles => _context.Vehicles;
Here, the Vehicles property returns the Vehicles DbSet from the DbContext. And, as we have already noted a DbSet inherits from an IQueryable
so it easily maps to the IQueryable
return type of the repo Vehicles property.
But for the unit test we had to mock the Vehicle repository.
To do this, in the Can_Access_VehicleList_FromVehicleRepo
unit test method of the HomeControllerTests_Fails.cs
class file, we start off by instantiating an instance of a mocked IVehicleRespository using the Moq package we installed earlier.
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
Next, we have to specify what the Vehicles property of our mocked IVehicleRepository
will return. And, remember, it has to be of type IQueryable<Vehicle>
.
So, as shown below we use the Setup
method to specify which property (or method) of the mocked repo we want to specify behavior for, in this case Vehicles. Next, we specify what the Vehicles property should return using the Returns method.
FredsCars.Tests\Controllers\HomeControllerTests_Fails.cs
... existing code ...
mockVehicleRepo.Setup(mvr => mvr.Vehicles).Returns((new Vehicle[]
{
new Vehicle
{
Id = 1,
Make = "Make1",
Model = "Model1",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
},
},
new Vehicle
{
Id = 2,
Make = "Make2",
Model = "Model2",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
},
},
... rest of vehicles ...
}
).AsQueryable<Vehicle>());
... existing code ...
In the code above we specify an array of Vehicles to be returned by the Returns
method and at the end we convert it to an IQueryable<Vehicle>
with the AsQueryable<Vehicle>
method. This conversion is what would lead us to believe our repo is fine. Our mock repo should behave just like our runtime repo.
In the next line of the unit test we instantiate an instance of the HomeController and inject our mock repo into its constructor using the mock’s Object property.
HomeController controller = new HomeController(mockVehicleRepo.Object);
And finally, in the next line is where the error happens.
IEnumerable<Vehicle>? result =
(await controller.Index()).ViewData.Model
as IEnumerable<Vehicle>;
In the code above, we try to call the controller’s asynchronous Index method using the await keyword.
To see the resulting error by this call, set a breakpoint right after the return statement in the HomeController’s Index method by clicking in the left gray gutter.

Next, debug the unit test by right clicking on the unit test and selecting the run option in Test Explorer.

And we can see our infamous error with the same error message we looked at earlier.

It turns out there is just something about the nature of mocking an object to return an IQueryable
and trying to access those results asynchronously that can be an awkward process.
Write the Unit Test: Second Draft
In the last section we took a good shot at writing our unit test but got a red failing result. But that’s ok. As a matter of fact fact, this is perfectly normal. Usually this iterative process can help us discover bugs or bad coding logic in our code. In this case the cause of the error is a little different. We need to deal with the awkwardness of testing a mocked IQueryable result asyncronously.
Add the MockQueryable.Moq package
Luckily for us the Moq package has an extension to deal with this situation in a separate package called MockQueryable.Moq.
If you search for MockQueryable.Moq in Nuget.org and go to README tab it states:
Extensions for mocking Entity Framework Core (EFCore) operations such [as] ToListAsync, FirstOrDefaultAsync etc. by Moq, NSubstitute or FakeItEasy When writing tests for your application it is often desirable to avoid hitting the database. The extensions allow you to achieve this by creating a context – with behavior defined by your tests – that makes use of in-memory data.
The main point to take away here is that the Moq extensions in this package will help solve our problem of setting up a test to return an IQueryble
from a mocked repository via the ToListAsync
method.
Let’s install the MockQueryable.Moq package now. Open up a command window, point it to the FredsCarsTests project, and run the command below.
dotnet add package MockQueryable.Moq --version 7.0.3
Once the command completes, we can see that the Moq extensions package has been installed through Visual Studio.

Modify the test code
Create a new test class in the controllers folder of the FredsCarsTests project called HomeControllerTests.cs and modify it with the code shown below.
NOTE: Normally we would not create a file called HomeControllerTests_Fails.cs to put failing unit test code in a separate file. We would create the HomeControllerTests.cs file, write the original unit test code there, and watch it fail. Then correct the errors, refactor the code, and watch it fail until after as many iterations as needed until we get a green passing result. But I wanted you to be able to look at and compare the two versions because the first version is a standard unit testing setup you will see in many books and tutorials. So I wanted to take you through that process, watch it fail, create a working version, and be able to refer back and forth as well as completely understand why this standard setup would not work for us once we implemented asynchronous code in the Home Controllers Index method.
FredsCars\FredsCars.Tests\Controllers\HomeControllerTests.cs
using FredsCars.Controllers;
using FredsCars.Models;
using FredsCars.Models.Repositories;
using MockQueryable;
using Moq;
namespace FredsCars.Tests.Controllers
{
public class HomeControllerTests
{
[Fact]
public async Task Can_Access_VehicleList_FromVehicleRepo()
{
// Arrange
// 1 - create a List<T> with test items
var vehicles = new List<Vehicle>
{
new Vehicle
{
Id = 1,
Make = "Make1",
Model = "Model1",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
}
},
new Vehicle
{
Id = 2,
Make = "Make2",
Model = "Model2",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
}
},
new Vehicle
{
Id = 3,
Make = "Make3",
Model = "Model3",
VehicleType = new VehicleType
{
Id = 2,
Name = "Truck"
}
},
new Vehicle
{
Id = 4,
Make = "Make4",
Model = "Model4",
VehicleType = new VehicleType
{
Id = 3,
Name = "Jeep"
}
}
};
// 2 - build mock using MockQueryable.Moq extension
var mockVehiclesIQueryable = vehicles.BuildMock();
// 3 - build mock IVehicleRepository
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
mockVehicleRepo.Setup(mvr => mvr.Vehicles).Returns(mockVehiclesIQueryable);
HomeController controller = new HomeController(mockVehicleRepo.Object);
// Act
IEnumerable<Vehicle>? result =
(await controller.Index()).ViewData.Model
as IEnumerable<Vehicle>;
// Assert
Vehicle[] vehicleArray = result?.ToArray()
?? Array.Empty<Vehicle>();
Assert.True(vehicleArray.Length == 4);
int carCount = vehicleArray.Where(v => v.VehicleType.Name == "Car").Count();
Assert.Equal(2, carCount);
Assert.True(vehicleArray[2].Make == "Make3" &&
vehicleArray[2].Model == "Model3");
Assert.True(vehicleArray[3].Make == "Make4" &&
vehicleArray[3].Model == "Model4");
}
}
}
If you run the new test the same way we ran its counterpart earlier, the result shows a green passing result.

Inspect the test code
In the code above for the new successful unit test in the new test file, we imported the MockQueryable namespace at the top of the file. But notice we also need to keep the reference to the Moq namespace.
using MockQueryable;
using Moq;
The test method itself is marked as async so that it can call the controller’s asynchronous Index method using the await keyword. It also has the Fact attribute to indicate that it is a unit test that should be run by the test runner.
[Fact]
public async Task Can_Access_VehicleList_FromVehicleRepo()
{
... existing code ...
IEnumerable<Vehicle>? result =
(await controller.Index()).ViewData.Model
as IEnumerable<Vehicle>;
... existing code ...
}
AAA (Arrange-Act-Assert) pattern in unit testing
Inside the body of the test method we use a common approach to software testing called the AAA (Arrange-Act-Assert) pattern.
There are three sections to the body of a test method with this pattern.
Arrange: Set up the necessary components, preconditions, and inputs.
Act: Perform the action or invoke the method being tested.
Assert: Verify the expected outcome via “assertions“.
public async Task Can_Access_VehicleList_FromVehicleRepo()
{
// Arrange
// Setup test data, mocked repo, and instantiate the
// Home controller with the mock repo
// Act
// Use the HomeController component from the Arrange section
// to call its Index method and capture its results
// Assert
// Make assertions about the results from the Act section that must be true in
// order for the test to pass
Arrange Section
In the arrange section we create some test data, mock an IQueryable of Vehicle using the test data, mock an IVehicleRepository using the mock IQueryable, and finally instantiate the Home controller by injecting the mock IVehicleRepository into its constructor. This is our build up.
Create the test data
To create the test data we simply create a List of Vehicles much like we did in the failing version. The key difference here is that we don’t use the AsQueryable() method to convert the list to an IQueryable because we know this was the crux of our error. ASP.Net Core cannot call the results of a List or Array converted to IQueryable
by the AsQueryable
method though a mocked repo using the ToListAsync method in the Home controller’s Index method.
// Arrange
// 1 - create a List<T> with test items
var vehicles = new List<Vehicle>
{
new Vehicle
{
Id = 1,
Make = "Make1",
Model = "Model1",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
}
},
new Vehicle
{
Id = 2,
Make = "Make2",
Model = "Model2",
VehicleType = new VehicleType
{
Id = 1,
Name = "Car"
}
},
new Vehicle
{
Id = 3,
Make = "Make3",
Model = "Model3",
VehicleType = new VehicleType
{
Id = 2,
Name = "Truck"
}
},
new Vehicle
{
Id = 4,
Make = "Make4",
Model = "Model4",
VehicleType = new VehicleType
{
Id = 3,
Name = "Jeep"
}
}
};
Mock the IQueryable<Vehicle> using MockQueryable package
The second thing we do in the arrange section is mock the IQueryable<Vehicle> by converting the List test data using the MockQueryable extension of the Moq package. This seems to be the step that fixes the error. After his point, the two versions of our test are basically the same.
// 2 - build mock using MockQueryable.Moq extension
var mockVehiclesIQueryable = vehicles.BuildMock();
Build the mock IVehicleRepositoy
The third thing we do in the arrange section is build the mock IVehicleRepository by first instantiating an instance of Mock<IVehicleRepository> and then using the mock Setup and Returns methods to specify that the mocked Vehicle Repo’s Vehicles property returns the mocked IQueryable<Vehicle>. This differs from the original test where the IQueryable being returned was an Array<Vehicle> converted to an IQueryable with the AsQueryable method.
// 3 - build mock IVehicleRepository
Mock<IVehicleRepository> mockVehicleRepo =
new Mock<IVehicleRepository>();
mockVehicleRepo.Setup(mvr => mvr.Vehicles).Returns(mockVehiclesIQueryable);
Instantiate the HomeController
Finally in the arrange section we instantiate an instance of the HomeController class and inject into its constructor the mocked Vehicle repo containing the mock IQueryable<Vehicle>
HomeController controller = new HomeController(mockVehicleRepo.Object);
Again, note we actually inject the Object property of the mock Vehicle repo.
Act Section
Now that we have “Arranged” and built up all of the objects we need for the unit test, we can “Act” on them.
The single statement in the Act section is the same as it was in the failing test except for the fact that now it works.
// Act
IEnumerable<Vehicle>? result =
(await controller.Index()).ViewData.Model
as IEnumerable<Vehicle>;
Here, we instantiate a variable called result of type nullable IEnumerable of Vehicle
to capture the results, make the asynchronous call to the Home controller’s Index method using the await keyword, grab the DataDictionary (named ViewData) of the ViewResult being returned, get its Model and convert the model (IList<Vehicle>
) to an IEnumerable
of Vehicle. The IEnumerable<Vehicle> result is assigned to the result variable.
Assert Section
In the Assert section we start off by converting the results from the Act section to an Array to make it easier to work with and assign it to a variable of type Vehicle array named vehicleArray. If the result happens to be null, we use the null coalesce operator to assign an empty Array<Vehicle> to the result variable.
Vehicle[] vehicleArray = result?.ToArray()
?? Array.Empty<Vehicle>();
Finally, in the last four statements we can use the static methods of xUnit’s Assert object to verify our expected outcomes about the Vehicle array based on the test data we fed in.
The first Assert statement uses the Assert.True
static method to verify the length of the Vehicle array is 4.
Assert.True(vehicleArray.Length == 4);
In the next two statements we use a LINQ query on vehicleArray to determine how many Vehicles in the vehicleArray variable are of type “Car” and assign the result to a variable named carCount of type int.
Then we use the static Assert.Equal
method to verify the actual value in the carCount variable matches our expected amount of 2.
int carCount = vehicleArray.Where(v => v.VehicleType.Name == "Car").Count();
Assert.Equal(2, carCount);
The last two statements verify what we believe the Make and Model of the third and fourth elements in the array should be based on the test data we fed in.
Remember arrays are 0 index based to vehicleArray[2] is the third vehicle and vehicleArray[3] accesses the fourth vehicle.
Assert.True(vehicleArray[2].Make == "Make3" &&
vehicleArray[2].Model == "Model3");
Assert.True(vehicleArray[3].Make == "Make4" &&
vehicleArray[3].Model == "Model4");
At this point feel free to completely comment out the HomeControllerTests_Fails.cs
file so its test will not show up in the Test Explorer anymore.
Assert methods
In the last section we saw how to use some static Assert methods to verify the outcome of our tests.
The Assert
class provides a variety of assertion methods to validate expected outcomes in unit tests. The following are a few of the most commonly used methods.
Common xUnit Assert Methods
Equality Assertions
Assert.Equal(expected, actual)
: Verifies that two values are equal.Assert.NotEqual(expected, actual)
: Verifies that two values are not equal.
Assert.Equal(10, 5 + 5); // Passes
Assert.NotEqual(10, 4 + 5); // Passes
Boolean Assertions
Assert.True(condition)
: Passes if the condition istrue
.Assert.False(condition)
: Passes if the condition isfalse
.
Assert.True(5 > 3); // Passes
Assert.False(2 > 5); // Passes
Null Assertions
Assert.Null(object)
: Passes if the object isnull
.Assert.NotNull(object)
: Passes if the object is notnull
.
object obj = null;
Assert.Null(obj); // Passes
obj = new object();
Assert.NotNull(obj); // Passes
There are quite a few Assert methods actually. Remember you can always use Intellisense to look for one that fits your need.
What’s Next
As usual we accomplished quite a bit in this module. We setup an xUnit project, installed the Moq package, mocked up our dependencies, and wrote our first test and watched it fail. We talked about the difficulties of mocking and testing asynchronous code.
Then we installed the MockQueryable.Moq package to help us with this issue, refactored our test and got a succeeding result.
Now that the HomeController is unit tested we should really also unit test the repository itself since it is a service. And that is what we are going to do in the next module.