In the last module we unit tested the HomeController component.
In this module we will unit test the EFVehicleRepository service.
Create the Repositories test folder
Create a new folder on the root of the FredsCars.Tests project and name it Models and create a new subfolder under it named Repositories.
Create the Repo unit test file
In the new Repositories folder create a new class file called EFVehicleRepositoryTests.cs.

Write the Unit Test
Modify EFVehicleRepositoryTests.cs with the code below.
FredsCars\FredsCars.Tests\Models\Repositories\EFVehicleRepositoryTests.cs
using FredsCars.Data;
using FredsCars.Models;
using FredsCars.Models.Repositories;
using MockQueryable.Moq;
using Moq;
namespace FredsCars.Tests.Models.Repositories
{
public class EFVehicleRepositoryTests
{
[Fact]
public void Can_Return_VehicleList()
{
// 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 DbSet
var mockVehiclesDbSet = vehicles.AsQueryable().BuildMockDbSet();
// 3 - mock FredsCarsDbSet
Mock<FredsCarsDbContext> mockFCDbContext =
new Mock<FredsCarsDbContext>();
mockFCDbContext.SetupGet(m => m.Vehicles)
.Returns(mockVehiclesDbSet.Object);
// 4 - instantiate EFVehicleRepository
EFVehicleRepository repo
= new EFVehicleRepository(mockFCDbContext.Object);
// Act
IQueryable<Vehicle> vehicleList = repo.Vehicles;
// Assert
Vehicle[] vehicleArray = vehicleList.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");
}
}
}
Fix the System.NotSupportedException – Non-overridable members error.
If you run the Can_Return_VehicleList test at this point, you get an error message that says:
Message:
System.NotSupportedException : Unsupported expression: m => m.Vehicles
Non-overridable members (here: FredsCarsDbContext.get_Vehicles) may not be used in setup / verification expressions.
Let’s inspect the code and see if we can uncover the reason for and meaning of this error.
In the arrange section we first create a List of Vehicles representing our test data much like the first unit test we wrote.
Unlike the last unit test where we used the MockQueryable package to convert the test data to an IQueryable for the repo to return, here we convert the test data to a DbSet for a mocked FredsCarsDbContext to return via its Vehicles property.
var mockVehiclesDbSet = vehicles.AsQueryable().BuildMockDbSet();
Again, the MockQueryable package comes in handy with it’s BuildMockDbSet
method shown in the above line. Notice we first have to convert the vehicles variable to an IQueryable with the AsQueryable method.
The next step is where we get the error. Now that we have a mocked DbSet of Vehicle test data, we should be able to return it from a mocked FredsCarsDbContext via its Vehicles property. Right?
Mock<FredsCarsDbContext> mockFCDbContext =
new Mock<FredsCarsDbContext>();
mockFCDbContext.SetupGet(m => m.Vehicles)
.Returns(mockVehiclesDbSet.Object);
In the above code we use the SetupGet method rather than Setup because the Vehicles DbSet property of FredsCarsDbContext is readonly meaning it only implements the Get accessor of the property and not the Set accessor. But it still does not fix our problem.
Lambda property expressions in C#
The crux of the error is that Vehicles is a read only property of FredsCarsDbContext.
public DbSet<Vehicle> Vehicles => Set<Vehicle>();
In the above line we use the Lambda “goes into” (=>
) operator to create a property expression that returns a DbSet<Vehicle>. The Set<Vehicle> method creates the DbSet that can be used to create and save instances of Vehicle.
In C#, lambda property expressions allow you to define read-only properties, providing a concise and expressive way to implement properties. These are useful for simple computed properties that return a value without needing a backing field.
And this is what the error message is trying to tell us. Here is the error message once again.
Message:
System.NotSupportedException : Unsupported expression: m => m.Vehicles
Non-overridable members (here: FredsCarsDbContext.get_Vehicles) may not be used in setup / verification expressions.
The FredsCarsDbContext.Vehicles
property is read only and therefor we cannot override its Get accessor.
Virtual Properties in C#
In order to fix this error we need to make the Vehicles DbSet property in FredsCarsDbContext virtual so we can override it. Modify the FredsCarsDbContext with the code below.
FredsCars\FredsCars\Data\FredsCarsDbContext.cs
... existing code ...
public FredsCarsDbContext(DbContextOptions<FredsCarsDbContext> options)
: base(options)
{}
public virtual DbSet<Vehicle> Vehicles => Set<Vehicle>();
public DbSet<VehicleType> VehicleTypes => Set<VehicleType>();
... existing code ...
In the code above we simply added the virtual reserved word to the Vehicles property.
In C#, a virtual property is a property that can be overridden in derived classes. This is useful in inheritance when you want to allow subclasses to provide their own implementation of a property while still maintaining a common interface in the base class.
Here we allowed the Moq package to override the Vehicles property in order to create our mocked FredsCarsDbContext.
Fix the System.ArgumentException – Cannot instantiate proxy (FredsCarsDbContext
If we run the test at this point we now get another error:
Message:
System.ArgumentException : Can not instantiate proxy of class: FredsCars.Data.FredsCarsDbContext.
Could not find a parameterless constructor. (Parameter ‘constructorArguments’)
—- System.MissingMethodException : Constructor on type ‘Castle.Proxies.FredsCarsDbContextProxy’ not found.
This happens at line 74 in the test.
EFVehicleRepository repo
= new EFVehicleRepository(mockFCDbContext.Object);
It’s saying that even though we have fixed the read only property issue, Moq still cannot instantiate an instance of FredsCarsDbContext to inject into an EFVehcileRepository because the only constructor available expects a parameter of type DbContextOptions<FredsCarsDbContext
.
But for our test we are not not using a real database so we do not need the Db options information. We mocked the DbSet we need to return instead. To fix this error modify FredsCarsDbContext with the code shown below.
FredsCars\Data\FredsCarsDbContext.cs
using FredsCars.Models;
using Microsoft.EntityFrameworkCore;
namespace FredsCars.Data
{
public class FredsCarsDbContext : DbContext
{
public FredsCarsDbContext()
{
}
public FredsCarsDbContext(DbContextOptions<FredsCarsDbContext> options)
: base(options)
{}
public virtual DbSet<Vehicle> Vehicles => Set<Vehicle>();
public DbSet<VehicleType> VehicleTypes => Set<VehicleType>();
... existing code ...
In the above code we simply added a parameterless constructor to FredsCarsDbContext for the unit test and Moq to use.
If we run the test at this point it should give a passing result.
The Act Section
With the arrange section all setup and doing its job to set up the needed mocks and instantiate an EFVehicleRepository instance, we can now call the repo’s Vehicles property and assign the results to an IQueryable<Vehicle>
.
// Act
IQueryable<Vehicle> vehicleList = repo.Vehicles;
The Assert Section
The assert section is the same as in the first unit test of the Home Controller’s Index method. We convert vehilceList results from an IQueryable<Vehicle> to an array and all the assertions are the same since we use the same test data.
What’s Next
In this module we unit tested the EFVehicleRepository class’s implementation of the IVehicleRepository. Now that our project is fully unit tested up to this point we can return back to working on the features of the applicaiton.
In the next section we are going to add a thumbnail image of each vehicle to its record in the results. We will need to put these images under the wwwroot folder and add a migration to add a property to the Vehicle entity class containing the pathway to its image.