Moqzilla
I wrote a class library called Moqzilla. It's a tool that will automatically mock dependencies that are part of the constructor of the tested class.
I wrote a class library called Moqzilla. It's a tool that will automatically mock dependencies that are part of the constructor of the tested class. It's available on Nuget right now.
Why?
There are plenty of other similar solutions out there. If you google automatic mocking for unit tests, you'll find them easily. I tried using some of these and found that they didn't meet my requirements. These requirements are:
- Support .NET Standard
- Provide direct access to Moq objects
Things I've Tried
Below, I'll discuss the libraries I've tried and did not succeed with.
AutoMoq
This is probably the closest to what I was looking for. It allows you to use the GetMock
method to retrieve a mock from the context, and use Create
to automatically populate the constructor for a test subject with mocks.
Reasons I like it:
- It returns Moq objects directly.
- It's simple to use and alleviates the frustration of having to update all tests when dependencies are added to the tested class.
Reasons it won't work:
- If I want to target .NET Standard, I'm out of luck.
- It introduces another dependency called Unity, which doesn't build for .NET Standard out of the box.
- AutoMoq itself doesn't build for .NET Standard.
Further information: AutoMoq
AutoFixture's AutoMoq Extension
AutoFixture is great for generating test data. In fact, I use it professionally to generate random records with ease. They've introduced a library called AutoMoq that supposedly does what I'm trying to accomplish.
Reasons I like it:
- It returns Moq objects directly.
- It integrates seamlessly with AutoFixture, which I already use.
Reasons it won't work:
- It's a bit cumbersome to use and ends up requiring the same amount or more code than if I'd just created all the mocks myself.
Further information: AutoFixture.AutoMoq
The Development of Moqzilla
So, let's come back to Moqzilla. I borrowed the general implementation design from AutoMoq. I won't go into how to use Moqzilla; you can check out the Github page for that. Here, I'm going to go over the internals.
Reflection Works Differently Per Framework
The Reflection class/method layout works a little differently in .NET Standard. In order to get more detailed information about a type, you need to call GetTypeInfo()
on it. That'll get you a TypeInfo
.
In order to make the compiler happy, I had to wrap certain things in functions with compiler directives:
/// <summary>
/// Returns TRUE if the specified type is an interface type.
/// </summary>
private static bool TypeIsInterface(Type type)
{
#if NETSTANDARD1_3
return type.GetTypeInfo().IsInterface;
#else
return type.IsInterface;
#endif
}
Moq Requires Mock Construction With Generic Types
In order to keep track of mocks within the context, I used a Dictionary<Type, object>
. We can't use Mock<>
, because C# requires that we specify an inner type when referring to a generic type like this. So, we're faithfully casting. Thankfully, creating a new Mock object is easy, because the type is passed in with the method call:
/// <summary>
/// Get a mock from the container.
/// </summary>
public Mock<TSubject> Mock<TSubject>()
where TSubject : class
{
var type = typeof(TSubject);
if (_mockRepository.ContainsKey(type))
return (Mock<TSubject>) _mockRepository[type];
var mock = new Mock<TSubject>();
_mockRepository[type] = mock;
return mock;
}
The Moq library doesn't just let you create a mock by passing a type object in as a parameter. It requires that you specify the type in its generic constructor. When we're constructing types to fill in as constructor parameters, all we have to work with is a Type
object, nothing more! Since these objects can't be converted to generic type parameters in the language, we have to use reflection:
/// <summary>
/// Get a mock from the repository for a type determined at runtime.
/// </summary>
protected Mock Get(Type type)
{
// Return a cached mock, if we have one.
if (_mockRepository.ContainsKey(type))
return (Mock)_mockRepository[type];
// Create a mock - reflection is needed for invocation due to how Moq works.
var mock = typeof(Mock<>)
.MakeGenericType(type)
.GetConstructor(EmptyTypeArray)
?.Invoke(EmptyObjectArray);
_mockRepository[type] = mock;
return (Mock)mock;
}
These two methods should cover every way we need to construct a Mock object.
Activators
Suppose a unit test requires some kind of easy setup that needs to be done whenever a mock is initialized, before the test subject is constructed. We need some code to run. The methods that run here are called activators.
I am once more using a Dictionary<Type, object>
to hold all the activators. The activators are chained in the order they are attached, so they can be overridden. Like before, there's nothing in the C# language that will allow us to pass a Type
object as a generic type parameter, so we just trust that the cast will succeed.
/// <summary>
/// Registers an activation. When <see cref="Create{TSubject}"/> is invoked,
/// activations are invoked before object creation. This is used to set up consistent parts
/// of a mock.
/// </summary>
public void Activate<TSubject>(Action<Mock<TSubject>> activator)
where TSubject : class
{
var type = typeof(TSubject);
if (_activatorRepository.ContainsKey(type))
{
// Chain existing activations.
var currentActivation = (Action<Mock<TSubject>>)_activatorRepository[type];
_activatorRepository[type] = (Action<Mock<TSubject>>)(mock =>
{
currentActivation(mock);
activator(mock);
});
}
else
{
_activatorRepository[type] = activator;
}
}
Invoking the Constructor with Our Mocks
Now, we're on to the real meat of the library: creating the test subject, filling in all interface dependencies with mocks that we've created using the Moq library.
Again, this gets crazy with reflection. We use it to get the list of constructors that are available. We use it to determine if the constructor is one that consists of only mockable interfaces. We use it to invoke activators. Finally, we use it to invoke the constructor itself. The biggest headache here is the inability to use Type
objects as generic type parameters again.
/// <summary>
/// Creates an object, mocking up all dependencies in the constructor.
/// </summary>
/// <exception cref="MockerException">Thrown when no mockable constructor could be found.</exception>
public TSubject Create<TSubject>()
where TSubject : class
{
// Cache constructor parameters.
var type = typeof(TSubject);
var constructors = type.GetConstructors();
var parameters = constructors
.ToDictionary(c => c, c => c.GetParameters());
// Determine which constructors we can use.
var validConstructors = constructors
.Where(c => parameters[c]
.All(p => TypeIsInterface(p.ParameterType)))
.ToArray();
// If there are no constructors, fail.
if (!validConstructors.Any())
throw new MockerException(MockerExceptionType.NoValidConstructors);
// Choose the constructor with the most dependencies.
var mostSpecificConstructor = constructors
.OrderByDescending(c => parameters[c].Length)
.First();
// Pull mocked objects for the constructor from the repository.
var constructorArguments = parameters[mostSpecificConstructor]
.Select(p => Get(p.ParameterType).Object)
.ToArray();
// Run activations.
foreach (var parameter in parameters[mostSpecificConstructor])
{
if (!_activatorRepository.ContainsKey(parameter.ParameterType))
continue;
var activator = _activatorRepository[parameter.ParameterType];
var activatorType = activator.GetType();
activatorType.GetMethod("Invoke").Invoke(activator, new object[] { Get(parameter.ParameterType) });
}
// Instantiate the object.
return (TSubject)mostSpecificConstructor.Invoke(constructorArguments);
}
Using Moqzilla Looks Like...
[Test]
public void MyTest()
{
// Arrange.
var mocker = new Mocker();
var myObject = mocker.Create<MyClass>();
var mockedDependency = mocker.Mock<IDependency>();
mockedDependency.Setup(m => m.GetTheNumber()).Returns(4);
// Act.
var observedValue = myObject.GetDoubleTheNumber();
// Assert.
Assert.AreEqual(8, observedValue);
}
If for some reason, in the future, additional dependencies are added to MyClass
, I don't have to go around changing test setups. These new dependencies are automatically mocked with default behavior.
Conclusion
Moqzilla, like many other class libraries, grew from a specific need. I'm very pleased to say it's already found some use in both personal and professional projects. While writing Moqzilla, I learned a lot about Reflection and its changes in .NET Standard. I learned a lot about Nuget packaging and deployment. It's been a very rewarding experience.
I hope you enjoy using this for unit testing as much as I enjoyed writing it and learning all the while.