We put in a lot of time and effort in the last module to make our Vehicle controller’s HTTP Get method clean, DRY, and testable. So in this module we are going to start unit testing the controller. We learn all about unit testing in Part 2 of this book. But, this will be a good review and a good place to put unit testing into use for the back-end of our full-stack implementation of FredsCars.
- Choosing a Unit Testing Framework
- Create the Test Project
- Add a reference to the API project
- Identify components to test
- Unit Test the extension method
- Install the MockQueryable.Moq package
- Run the CanConvertVehiclesToDtos Test
- Unit Test the ApiResult class
- Unit Test the Vehicles Controller HTTP GET method
- What's Next
Choosing a Unit Testing Framework
.Net provides three unit testing Frameworks.
- mstest
- nUnit
- xUnit
All of the .Net included test frameworks are very similar.
We are going to continue to use xUnit as we did in Part 2.
Create the Test Project
Right click the FredsCars solution folder in Solution Explorer and select Add --> New Project
.
In the “Add a new project” dialogue, search for xunit.” Select the xUnit Test Project Type from the results and click Next. (Again make sure you pick the project type for C# and not VB or F#. Also make sure to select xUnit rather than nUnit.)

In the “Configure our new project” dialogue, name the project FredsCarsAPI.Tests and click the Next button. This is the convention for naming ASP.Net Core test projects.
[ProjectToBeTested].Tests
In the “Additional information” dialogue, select .Net 7.0 for the Framework and click Create.

At this point you should be able to see the new FredsCarsAPI.Tests project under the main solution in Solution Explorer

The test project creates a file called UnitTest1.cs and sets up a sample skeleton for your first test. We can go ahead and delete this file.
Add a reference to the API project
Within the new xUnit test project, we need to add a reference to the FredsCarsAPI project (the project to be tested) so we can mock or instantiate components and services we want to test.
Right click on the FredsCarsAPI.Tests project and select Add --> Project Reference
.
In the Reference Manager dialogue, select the FredsCarsAPI project and click OK.

Within Solution Explorer you should now see the reference under Projects to the FredsCarsAPI project under the Dependencies section in the FredsCarsAPI.Tests project.

Identify components to test
At this point in the FredsCarsAPI project we really have three pieces of code we need to test.
- The Vehicle controller itself.
- The ApiResult class
- The extension method to convert simple Vehicle objects to VehicleDTO objects.
The Vehicle Controller uses the ConvertVehiclesToDTOs extension method to prepare the query to send to the ApiResult class. It seems like our custom extension method is the bottom most dependency. So let’s start with that.
Unit Test the extension method
Create a new folder on the root of the FredsCarsAPI.Tests project called ExtensionMethods. In the new folder create a class file called ExtensionMethodTests.cs and fill it with the contents below.
FredsCarsAPI.Tests/ExtensionMethods/ExtensionMethodTests.cs
namespace FredsCarsAPI.Tests.ExtensionMethods
{
public class ExtensionMethodTests
{
#region ConvertVehiclesToDTOs Tests
[Fact]
public void CanConvertVehiclesToDtos()
{
}
#endregion
}
}
I used the region
preprocessor directive to mark the area where we will conduct tests specific to the ConvertVehiclesToDTOs()
extension method.
Next we have set up a method called CanConvertVehiclesToDtos
which describes the fact that we are testing the extension method to see if it in fact, “Can Convert Vehicles To Dtos”. We have applied the Fact
attribute to the test method to mark it as an xUnit test.
The extension method we are testing, ConvertVehiclesToDTOs()
takes in an IQueryable<Vehicle>
as a parameter. This parameter can be thought of as a dependency of the method we are testing. In the real application, IQueryable<Vehicle>
is built up by calling the Vehicles DbSet from the DbContext which returns the IQueryable. But we don’t want to work with a real DbContext and DbSet or the real database. We need to control the dependancy to ensure it is not causing any potential bugs of the actual component we are testing, in this case, the extension method.
We need a way to “fake” or “mock” the dependency. In part two we learned how to use a mocking tool called moq. Here, we are going to use a mocking package called MockQueryable.Moq which extends moq and makes it a little easier to work with IQueryables and DbSets called MockQueryable.
Install the MockQueryable.Moq package
Open a command prompt, navigate to the FredsCarsAPI.Tests directory and install the MockQueryable.Moq package by running the following commands.
cd C:\Development\FredsCars\FullStack\Module24\FredsCarsAPI.Tests
dotnet add package MockQueryable.Moq --version 7.0.0
Next, return to the ExtensionMethodTests.cs class file and modify its contents with the following.
FredsCarsAPI.Tests/ExtensionMethods/ExtensionMethodTests.cs
using FredsCarsAPI.Models;
using FredsCarsAPI.Models.DTOs;
using Microsoft.EntityFrameworkCore;
using MockQueryable.Moq;
namespace FredsCarsAPI.Tests.ExtensionMethods
{
public class ExtensionMethodTests
{
#region ConvertVehiclesToDTOs Tests
[Fact]
public void CanConvertVehiclesToDtos()
{
// Arrange
// 1 - create a List<T> with test items
var Vehicles = new List<Vehicle>()
{
new Vehicle { Id = 1,
Status = Status.New,
Year = "2022",
Make = "M1",
Model = "M1",
Color = "C1",
Price = 64000,
VIN = "123",
VehicleTypeId = 1,
VehicleType = new VehicleType { Id = 1, Name = "Car" } },
new Vehicle { Id = 2,
Status = Status.New,
Year = "2022",
Make = "M2",
Model = "M2",
Color = "C2",
Price = 64000,
VIN = "456",
VehicleTypeId = 2,
VehicleType = new VehicleType { Id = 2, Name = "Truck" } },
new Vehicle { Id = 3,
Status = Status.New,
Year = "2022",
Make = "M3",
Model = "M3",
Color = "C3",
Price = 64000,
VIN = "789",
VehicleTypeId = 3,
VehicleType = new VehicleType { Id = 3, Name = "Jeep" } }
};
// 2- Create the mock DbSet from the test data
var mockVehiclesDbSet = Vehicles.AsQueryable().BuildMockDbSet();
// 3- Convert the DbSet to an IQueryable interface
var vehiclesIQueryable = mockVehiclesDbSet.Object.AsQueryable();
// Act
var vehicleDTOs = vehiclesIQueryable
.Include(v => v.VehicleType)
.ConvertVehiclesToDTOs();
// Assert
Assert.IsType<IQueryable<VehicleDTO>>(vehicleDTOs);
}
#endregion
}
}
Run the CanConvertVehiclesToDtos Test
Run the test with Test Explorer
From the top menu in Visual Studio select View --> Test Explorer
.
On the left side of the Test Explorer Window, expand the Test tree all the way down to the CanConvertVehiclesToDtos Test, right click on the test and select run.

At this point the test fails as shown in the following screen shot.

But that’s ok. That’s why we run tests. Right? To uncover any hidden or potential bugs in our code. However, we don’t always want to assume a test fails because of the application code. The error may lay in the test code itself. We might have to step back and re-evaluate the test. So let’s do that. Let’s inspect the test code above.
First of all note we are using the A/A/A method:
- Arrange: set up objects and conditions we need for the test such as a list of test data.
- Act: perform actions for the test
- Assert: verify the expected outcome based on the result of Assert, Verify statements.
So, in the arrange section, the first thing we do is set up some test data as a List of Vehicles or a List<Vehicle>
and assign it to a variable called Vehicles.
Next, we create a mock DbSet of Vehicles using List<T>.AsQueryable()
method which converts a list to an IQueryable and then chain on the MockQueryable.Moq()
method provided by the new MockQueryable.Moq package.
var mockVehiclesDbSet = Vehicles.AsQueryable().BuildMockDbSet();
Then we convert the mocked DbSet back to a mocked IQueryable of Vehicles and assign the result to a variable called vehiclesIQueryable.
In the act section we use the mocked vehiclesIQueryable variable to include VehicleType objects for each Vehicle object just as before in the actual application code so we can try and convert Vehicles to VehicleDTOs. Then we chain on with LINQ fluid API the actual ConvertVehiclesToDTOs extension method we are actually trying to test and assign the result to a variable called vehicleDTOs.
var vehicleDTOs = vehiclesIQueryable
.Include(v => v.VehicleType)
.ConvertVehiclesToDTOs();
Finally, in the assert section, we assert that vehicleDTOs is of type IQueryable<VehicleDTO> which would mean that the ConvertVehiclesToDTOs extension method worked.
Assert.IsType<IQueryable<VehicleDTO>>(vehicleDTOs);
Unfortunately at this point our test fails in the last screen shot shown above with a message of “Assert.IsType()
” failure.
The reason given is that although the expected and actual object types are the same (FredsCarsAPI.Models.DTOs.VehicleDTO
), the type of IEnumerable for that object are different. The expected IEnumerable type is System.Linq.IQueryable
. And the actual IEnumerable type is MockQueryable.EntityFrameworkCore.TestAsyncEnumerableEfCore
.
Run the test from the command line.
If you prefer, you can run the test from the command line to see the errors there with the following command. Just make sure your command prompt is pointing to the FredsCarsAPI.Tests project first.
dotnet test

Fix the Assert.IsType() error
Return once again to the ExtensionMethodTests.cs file and make the following modifications.
FredsCarsAPI.Tests/ExtensionMethods/ExtensionMethodTests.cs
/*** existing code ***/
// Act
var vehicleDTOs = vehiclesIQueryable
.Include(v => v.VehicleType)
.ConvertVehiclesToDTOs();
string typeName =
vehicleDTOs.GetType().GetGenericArguments()[0].FullName;
// Assert
//Assert.IsType<IQueryable<VehicleDTO>>(vehicleDTOs);
Assert.Equal(typeName, "FredsCarsAPI.Models.DTOs.VehicleDTO");
}
#endregion
}
}
In the code above we have added a string variable called typeName and assigned to it only the FullName of the object we want to get back an IEnumerable for.
Then we comment out the Assert.IsType()
statement and replace it with a regular Assert.Equal()
statement this time just comparing the typeName of we just fetched to what we know it should be; FredsCarsAPI.Models.DTOs.VehicleDTO
.
Now if you run the test again it should pass.

Unit Test the ApiResult class
If we look back at the ApiResult class, its factory method takes in an IQueryable<T> (where T should be VehicleDTO objects), a pageIndex, and a pageSize. The factory method then calls the ApiResult constructor which returns the five properties; Data, PageIndex, PageSize, TotalCount, and TotalPages. Let’s think of the Data property as a page (of data). And think of the other four properties as Paging Info.
Having reviewed the ApiResult class functionality, let’s ask ourselves, “What do we need to test for this class? Here is what I can come up with. Maybe you can come with some more at the end of this section and create some tests of your own.
- Can select a page.
- Can return paging info.
Create a new folder in the FredsCarsAPI.Tests project root called Data and in the new folder create a class file called ApiResultTests.cs and fill it with the contents below.
FredsCarsAPI.Tests/Data/ApiResultTests.cs
using FredsCarsAPI.Data;
using MockQueryable.Moq;
namespace FredsCarsAPI.Tests.Data
{
public class TestPageData
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
public class ApiResultTests
{
private List<TestPageData> _data = new List<TestPageData>()
{
new TestPageData { Id = 1, Name = "a" },
new TestPageData { Id = 2, Name = "b" },
new TestPageData { Id = 3, Name = "c" },
new TestPageData { Id =4, Name = "d" },
new TestPageData { Id =5, Name = "e" },
new TestPageData { Id =6, Name = "f" },
new TestPageData { Id =7, Name = "g" },
new TestPageData { Id =8, Name = "h" },
new TestPageData { Id =9, Name = "i" },
new TestPageData { Id =10, Name = "j" },
new TestPageData { Id = 11, Name = "k" },
new TestPageData { Id = 12, Name = "l" },
new TestPageData { Id = 13, Name = "m" },
new TestPageData { Id = 14, Name = "n" }
};
[Fact]
public async Task CanSelectPage()
{
// Arrange
// 1- Create the mock DbSet from the test data
var dataDbSet = _data.AsQueryable().BuildMockDbSet();
// 3- Convert the DbSet to an IQueryable interface
var dataIQueryable = dataDbSet.Object.AsQueryable();
// Act
var response = await ApiResult<TestPageData>.CreatAsync(
dataIQueryable, 2, 4);
// Assert
// if returns 4 records; correct pagesize.
Assert.Equal(4, response.Data.Count);
// 1st and last data ids of page 3 (pageIndex of 2 + 1)
// should be 9 and 12 from testPageData.
Assert.True(response.Data[0].Id == 9
&& response.Data[3].Id == 12);
}
[Fact]
public async Task CanReturnPagingInfo()
{
// Arrange
// 1- Create the mock DbSet from the test data
var dataDbSet = _data.AsQueryable().BuildMockDbSet();
// 3- Convert the DbSet to an IQueryable interface
var dataIQueryable = dataDbSet.Object.AsQueryable();
// Act
var response = await ApiResult<TestPageData>.CreatAsync(
dataIQueryable, 2, 4);
// Assert
// all assertions to match testPageData.
Assert.Equal(14, response.TotalCount);
Assert.Equal(4, response.TotalPages);
Assert.Equal(2, response.PageIndex);
Assert.Equal(4, response.PageSize);
}
}
}
If you run the tests in Test Explorer these two new tests along with the others should all pass.

Let’s take a look at the test code above.
Since our ApiResult class was designed to be generic, we created a class at the top of the file called TestPageData with two properties; Id, and Name. This is so we can test the generic capability with something other than VehicleDTO objects.
Within the ApiTestResultTests class, we first set up a List<TestPagedata>
as paging test data with 14 records and assign it to a private variable called _data. We do this outside of any test methods so we can use the same test data in both tests and not have to build it up twice.
Next comes the first test, CanSelectPage()
with the Fact
attribute applied marking the method as a test. We have also marked the test method as asynchronous with async keyword so we can call the asynchronous ApiResult factory method.
In the Arrange section we create convert the List<TestPageData>
to a mock IQueryable<TestPageData>
using the now familiar MockQueryable.Moq
package and apply it to a variable called dataIQueryable
.
In the Act section we call the ApiResult method asynchronously using the await
keyword passing it the mock IQueryable of TestPageData, a pageIndex of 2 (which is really page 3 since collections are 0 base indexed), and a pageSize of 4. We then assign the result to a variable called response.
In the Assert section we use the response to test if four records were returned matching the pageSize we passed to the ApiResult factory method using the Data.Count
property of the response returned from the ApiResult constructor.
We then compare the first and last Ids of the test page data set returned which should be 9 and 12 for the third page of our test data. If all these assert conditions are true the test should pass. Which as we saw it has.
In the CanReturnPagingInfo()
method we build up the mock IQueryable<TestPageData>
in the same way as the previous test method and again assign it’s results to a variable called response.
In the Assert section we check that the TotalCount property of the response is 14 (the total count of our test data), the TotalPages property is 4 (TotalCount / pageSize rounded up), the PageIndex property is 2 (or the third page of data), and the PageSize property is 4 (the pageSize we passed in to the ApiResult call).
Unit Test the Vehicles Controller HTTP GET method
If we look at the GetVehicles()
method in the controller, it takes in pageIndex and pageSize as parameters, builds up the dataQuery as an IQueryable<Vehicle>
from the Vehicles DbSet of the DbContext, includes VehicleType information in the query, and then uses the ConvertVehiclesToDTOs()
extension method to convert the IQueryable<Vehicle> to IQueryable<VehicleDTO>
.
It can then use the built up dataQuery and pass it to the ApiResult factory method along with the incoming pageIndex and pageSize parameters.
We have already created Unit Tests for ConvertVehiclesToDTOs()
extension method and the ApiResult class, both of which can be thought of as dependencies of the Vehicles controller GetVehicles()
method since the Vehicle controller, “Depends” on these two components to function.
So, what exactly do we want to test in the Vehicles controller? I think the tests will be pretty similar to the tests we created for the ApiResult class. We want to be able to pass in pageIndex and pageSize parameters to the HTTP GET method, and return an ApiResult with the same five properties as before.
But, how do we go about this? We already know how to mock for the two dependencies we need to create the test. We learned that in the previous two sections.
The problem I see here is that dataQuery is declared and defined using the Vehicles DbSet of the DbContext. And the whole DbContext is received in the controller constructor through DI (Dependency Injection).
How can we isolate the Vehicles DbSet from the controller so we are only testing the controller and not the real DbSet and be sure the data is not introducing any potential bugs other then from the controller itself. Not to mentions we really don’t want to hit real data for a test when there could be millions of records out there. We want to use mocked data similar to the last round of testing.
Well, it turns out one of the main ways of isolating components in C# is through the use of interfaces.
I think it’s time to take another pit stop and do another round of refactoring in the Vehicles controller.
What’s Next
In the next module we are going to implement a pattern known as the Repository pattern. We are going to create a, “Vehicles Repository” and create a dependency on it in the Vehicles controller. This way when we go to test the Vehicles controller, we can mock a Vehicles DbSet as an interface and pass it in during the test.