Easy and fast extension of the Moq mocking framework for mocking and auto injection of classes when testing.
FAQs FastMoq API Documentation
public class Mocker {} // Primary class for auto mock and injection. This can be used standalone from MockerTestBase using Mocks property on the base class.
public abstract class MockerTestBase<TComponent> where TComponent : class {} // Assists in the creation of objects and provides direct access to Mocker.
public abstract class MockerBlazorTestBase<T> : TestContext, IMockerBlazorTestHelpers<T> where T : ComponentBase // Assists in the creation of Blazor components and provides direct access to Mocker.
public class CarTest : MockerTestBase<Car> {
[Fact]
public void TestCar() {
Component.Color.Should().Be(Color.Green);
Component.CarService.Should().NotBeNull();
}
}
public class Car {
public Color Color { get; set; } = Color.Green;
public ICarService CarService { get; }
public Car(ICarService carService) => CarService = carService;
}
public interface ICarService
{
Color Color { get; set; }
ICarService CarService { get; }
bool StartCar();
}
public class CarTest : MockerTestBase<Car> {
public CarTest() : base(mocks => {
mocks.Initialize<ICarService>(mock => mock.Setup(x => x.StartCar).Returns(true));
}
}
Auto injection allows creation of components with parameterized interfaces. If an override for creating the component is not specified, the component will be created will the default Mock Objects.
Additionally, the creation can be overwritten and provided with instances of the parameters. CreateInstance will automatically match the correct constructor to the parameters given to CreateInstance.
Mocks.CreateInstance(new MockFileSystem()); // CreateInstance matches the parameters and types with the Component constructor.
When multiple classes derive from the same interface, the Interface Type Map can map with class to use for the given injected interface. The map can also enable mock substitution.
public class TestClassDouble1 : ITestClassDouble {}
public class TestClassDouble2 : ITestClassDouble {}
This code maps ITestClassDouble to TestClassDouble1 when testing a component with ITestClassDouble.
Mocker.AddType<ITestClassDouble, TestClassDouble1>();
The map also accepts parameters to tell it how to create the instance.
Mocks.AddType<ITestClassDouble, TestClassDouble1>(() => new TestClassDouble());
Create Your DbContext. The DbContext can either have virtual DbSet(s) or an interface. In this example, we use virtual DbSets.
Suppose you have an ApplicationDbContext with a virtual DbSet<Blog>
:
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
// ...other DbSet properties
// This can be public, but we make it internal to hide it for testing only.
// This is only required if the public constructor does things that we don't want to do.
internal ApplicationDbContext()
{
// In order for an internal to work, you may need to add InternalsVisibleTo attribute in the AssemblyInfo or project to allow the mocker to see the internals.
// [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
}
// Public constructor used by application.
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) => Database.EnsureCreated();
// ... Initialize and setup
}
Suppose you want to test a repository class that uses the ApplicationDbContext:
public class BlogRepo(ApplicationDbContext dbContext)
{
public Blog? GetBlogById(int id) => dbContext.Blogs.AsEnumerable().FirstOrDefault(x => x.Id == id);
}
In your unit test, you want to override the SetupMocksAction to create a mock DbContext using Mocker. Here’s an example using xUnit:
public class BlogRepoTests : MockerTestBase<BlogRepo>
{
// This runs actions before BlogRepo is created.
// This cannot be done in the constructor because the component is created in the base class.
// An alternative way is to pass the code to the base constructor.
protected override Action<Mocker> SetupMocksAction => mocker =>
{
var dbContextMock = mocker.GetMockDbContext<ApplicationDbContext>(); // Create DbContextMock
mocker.AddType(_ => dbContextMock.Object); // Add DbContext to the Type Map.
// In this example, we don't use a IDbContextFactory, but if we did, it would do this instead of AddType:
// mocker.GetMock<IDbContextFactory<ApplicationDbContext>>().Setup(x => x.CreateDbContext()).Returns(dbContextMock.Object);
};
[Fact]
public void GetBlog_ShouldReturnBlog_WhenPassedId()
{
// Arrange
const int ID = 1234;
var blogsTestData = new List<Blog> { new() { Id = ID } };
// Create a mock DbContext
var dbContext = Mocks.GetRequiredObject<ApplicationDbContext>();
dbContext.Blogs.AddRange(blogsTestData); // Can also be dbContext.Set<Blog>().AddRange(blogsTestData)
// Act
var result = Component.GetBlogById(ID);
// Assert
Assert.NotNull(result);
Assert.True(result.Id.Equals(ID));
}
}
Run Your Tests: Execute your unit tests, and the mock DbContext will behave like a real DbContext, allowing you to test your BlogRepo logic without hitting the actual database.
In your unit test, you want to create a mock DbContext using Mocker. This can be done directly in the test if the context is either only needed for the method or the component is created manually. Here’s an example using xUnit:
public class BtnValidatorTests
{
[Fact]
public void BtnValidatorIsValid_ShouldReturnTrue()
{
// Arrange
var id = 1234;
var blogsTestData = new List<Blog> { new Blog { Id = id } };
// Create a mock DbContext
var mocker = new Mocker(); // Create a new mocker only if not already using MockerTestBase; otherwise use Mocks included in the base class.
var dbContextMock = mocker.GetMockDbContext<ApplicationDbContext>();
var dbContext = dbContextMock.Object;
dbContext.Blogs.Add(blogsTestData); // Can also be dbContext.Set<Blog>().Add(blogsTestData)
var validator = new BtnValidator(dbContext);
// Act
var result = validator.IsValid(id);
// Assert
Assert.True(result);
}
}
Run Your Tests: Execute your unit tests, and the mock DbContext will behave like a real DbContext, allowing you to test your BtnValidator logic without hitting the actual database.
Testing constructor parameters is very easy with TestAllConstructorParameters and TestConstructorParameters.
TestConstructorParameters
- Test the constructor that MockerTestBase used to create the Component for the test.
TestAllConstructorParameters
- Test all constructors in the component, regardless if the constructor was used to create the component for the test.
EnsureNullCheckThrown
- Tests the given action, parameter, and constructor parameter to ensure a NullArgumentException is thrown when null.
action
is the Action to run which must throw the ArgumentNullException
for the specified parameter. This is generally an action provided by TestConstructorParameters
or TestAllConstructorParameters
.parameter
is the name of the parameter that should throw the ArgumentNullException
. The exception must include the name of the parameter which is the default behavior of the ArgumentNullException
.constructor
(optional) is the constructor being tested. This is the string to display in test output; specifically is multiple constructors are tested.outputWriter
(optional) is the ITestOutputHelper
specific to XUnit (XUnit.Abstractions
)action.EnsureNullCheckThrown(parameter, constructor, outputWriter);
[Fact]
public void Service_NullArgChecks_AllConstructorsShouldPass() =>
TestAllConstructorParameters((action, constructor, parameter) =>
action.EnsureNullCheckThrown(parameter, constructor, outputWriter));
// Check values for null
[Fact]
public void Service_NullArgChecks() => TestConstructorParameters((action, constructorName, parameterName) =>
{
outputWriter?.WriteLine($"Testing {constructorName}\n - {parameterName}");
action
.Should()
.Throw<ArgumentNullException>()
.WithMessage($"*{parameterName}*");
});
// Check values for specific criteria.
[Fact]
public void Service_NullArgChecks() => TestConstructorParameters((action, constructorName, parameterName) =>
{
outputWriter?.WriteLine($"Testing {constructorName}\n - {parameterName}");
action
.Should()
.Throw<ArgumentNullException>()
.WithMessage($"*{parameterName}*");
},
info =>
{
return info switch
{
{ ParameterType: { Name: "string" }} => string.Empty,
{ ParameterType: { Name: "int" }} => -1,
_ => default,
};
},
info =>
{
return info switch
{
{ ParameterType: { Name: "string" }} => "Valid Value",
{ ParameterType: { Name: "int" }} => 22,
_ => Mocks.GetObject(info.ParameterType),
};
}
);
// Test constructors for null, using built-in extension and log the output.
[Fact]
public void TestAllConstructors_WithExtension()
{
var messages = new List<string>();
TestAllConstructorParameters((action, constructor, parameter) => action.EnsureNullCheckThrown(parameter, constructor, messages.Add));
messages.Should().Contain(new List<string>()
{
"Testing .ctor(IFileSystem fileSystem, String field)\n - fileSystem",
"Passed fileSystem",
"Testing .ctor(IFileSystem fileSystem, String field)\n - field",
"Passed field",
}
);
}
When testing method calls on a component, often the method’s parameters are mock objects or just need default values. Instead of maintaining the parameter list, methods can be called without specifying specific parameters until required.
FastMoq knows how mocks are already defined and the caller can use those mocks or their own provided mocks, if required.
The helper command, CallMethod
can be used to call any method with or without parameters.
Any method called with CallMethod
can be anywhere such as a component method or a static method. Given the following CallTestMethod
, it takes value and reference parameters, many of which can be mocked. If the value cannot be mocked, it can be defaulted.
public object?[] CallTestMethod(int num, IFileSystem fileSystem, ITestCollectionOrderer dClass, TestClassMultiple mClass, string name)
{
ArgumentNullException.ThrowIfNull(fileSystem);
ArgumentNullException.ThrowIfNull(dClass);
ArgumentNullException.ThrowIfNull(mClass);
ArgumentNullException.ThrowIfNull(num);
ArgumentNullException.ThrowIfNull(name);
return
[
num, fileSystem, dClass, mClass, name,
];
}
In this simple call, the method CallTestMethod
will be called with default values and the current mocks.
[Fact]
public void CallMethod()
{
var result = Mocks.CallMethod<object?[]>(Component.CallTestMethod);
result.Length.Should().Be(5);
result[0].Should().Be(0);
result[1].Should().BeOfType<MockFileSystem>().And.NotBeNull();
result[2].GetType().IsAssignableTo(typeof(ITestCollectionOrderer)).Should().BeTrue();
result[2].Should().NotBeNull();
result[3].GetType().IsAssignableTo(typeof(TestClassMultiple)).Should().BeTrue();
result[3].Should().NotBeNull();
result[4].Should().Be("");
}
In the previous call, CallMethod
attempts to use mock parameters and then default values. The value for num
was the default(int)
which is 0. The default string value is string.Empty
.
To override a value, parameters can be passed to the method. The parameters do not have to have the same count, but they do require the same order. For example, the following code calls CallTestMethod
with num parameter 4 instead of 0. All other parameters are defaulted to their mocks or value default.
[Fact]
public void CallMethod_WithParams()
{
var result = Mocks.CallMethod<object?[]>(Component.CallTestMethod, 4);
result.Length.Should().Be(5);
result[0].Should().Be(4);
result[1].Should().BeOfType<MockFileSystem>().And.NotBeNull();
result[2].GetType().IsAssignableTo(typeof(ITestCollectionOrderer)).Should().BeTrue();
result[2].Should().NotBeNull();
result[3].GetType().IsAssignableTo(typeof(TestClassMultiple)).Should().BeTrue();
result[3].Should().NotBeNull();
result[4].Should().Be("");
}
In the next call, the first two parameters are overridden only and the other values are the default mock objects/values.
[Fact]
public void CallMethod_WithParams2()
{
var result = Mocks.CallMethod<object?[]>(Component.CallTestMethod, 4, Mocks.fileSystem);
result.Length.Should().Be(5);
result[0].Should().Be(4);
result[1].Should().BeOfType<MockFileSystem>().And.NotBeNull();
result[2].GetType().IsAssignableTo(typeof(ITestCollectionOrderer)).Should().BeTrue();
result[2].Should().NotBeNull();
result[3].GetType().IsAssignableTo(typeof(TestClassMultiple)).Should().BeTrue();
result[3].Should().NotBeNull();
result[4].Should().Be("");
}
Exceptions can be caught just like the method was called directly. The example below shows the assert looking for an argument null exception.
[Fact]
public void CallMethod_WithException()
{
Assert.Throws<ArgumentNullException>(() => Mocks.CallMethod<object?[]>(Component.CallTestMethod, 4, null));
}
If the test returns:
Add the following InternalsVisibleTo
line to the AssemblyInfo file.
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]