A C# mocking library that generates mocks at compile-time using a source generator.
This can only be used in projects that use C# 9.0 (or higher), such as .NET 5.0 projects.
Run the following command in the NuGet package manager console
Install-Package MockSourceGenerator
or using the .NET cli
dotnet add package MockSourceGenerator
Learn how to use the mock source generator in just 5 easy steps.
Let's say we need a mock that implements the following interface:
public interface IExternalSystemService
{
public int Add(int operand1, int operand2);
}
We start by creating a test:
[Fact]
public void TestCase()
{
var mock = new MyMock();
// ...
}
That won't compile, because the MyMock
type doesn't exist. But be patient, because it will be there soon. Note that the type name must end with Mock
. Apart from that, you can choose any name you want (e.g.: ExternalSystemServiceMock
or GeneratedServiceMock
).
Let's add a cast, to indicate to the generator what type to mock:
[Fact]
public void TestCase()
{
var mock = (IExternalSystemService) new MyMock();
// ...
}
This will actually compile! The source generator will generate a class that can be used to start mocking things.
[Fact]
public void TestCase()
{
var mock = (IExternalSystemService) new MyMock
{
MockAdd = (o1, o2) => 5
};
// ...
}
For every member that can be mocked, another member will be generated with a Mock
prefix. In this case, MockAdd
allows you to set an implementation. In this case I chose for the method to always return 5, but anything can be done there (including throwing an exception if something unexpected is passed as an argument).
I used an object initializer here, though this is not required. I personally like doing it this way, because it makes it clear how the mock will function in just 1 statement.
[Fact]
public void TestCase()
{
var mock = (IExternalSystemService) new MyMock
{
MockAdd = (o1, o2) => 5
};
Assert.Equal(5, mock.Add(2, 3));
}
Now the test is done. It's not very useful, as it just tests the mock itself. But I hope you can see how easy it is to use this method to inject your mock into a class that is being tested.
There are more things you can do, which I'll explain below. But I hope this basic tutorial helps you get started.
By default, you must mock all methods that are called during the test. If you don't mock something, an exception will be thrown. You can opt-out of this by setting ReturnDefaultIfNotMocked
to true. Anything that is not mocked, will then return the default value of the return type.
[Fact]
public void TestCase2()
{
// The following does not provide a mock implementation for Add(...)
var mock = (IExternalSystemService) new MyMock
{
ReturnDefaultIfNotMocked = true
};
Assert.Equal(0, mock.Add(-2, 2)); // 0 is the default int value
}
You can also verify if the mocked methods are called (and in which order, with which parameters, if you want).
[Fact]
public void TestCase3()
{
var mock = (IExternalSystemService) new MyMock
{
MockAdd = (o1, o2) => 5
};
Assert.Equal(5, mock.Add(2, 3));
Assert.Equal(5, mock.Add(1, 4));
// Check if the Add(...) method is called twice, with given parameters (optional)
Assert.Collection(
((MyMock) mock).HistoryEntries,
i => Assert.Equal("Add(2, 3)", i.ToString()),
i => Assert.Equal("Add(1, 4)", i.ToString()));
}
Well, I think this is a good place to start from. If you are excited about this or have any ideas on how to improve things, please let me know on Twitter!
Some links that you might like: