Tethos is automated auto-mocking system which utilizes Castle.Windsor as backbone for working with mocked dependencies used during unit testing. It is test framework agnostic. Tethos supports all popular mocking libraries - Moq, NSubstitute and FakeItEasy:
| Package | NuGet |
|---|---|
| Tethos.Moq | |
| Tethos.FakeItEasy | |
| Tethos.NSubstitute |
Consider following example:
public class SystemUnderTest
{
public SystemUnderTest(IMockA mockA, IMockB mockB, IMockC mockC)
{
...
}
public int Exercise()
{
MockA.Get();
MockB.Get();
MockC.Get();
}
}in order to resolve dependencies for SystemUnderTest we will need to write following unit test:
[Fact]
public void Test()
{
var sut = new SystemUnderTest(
Mock.Of<IMockA>(),
Mock.Of<IMockB>(),
Mock.Of<IMockC>()
);
}with Tethos all you need to do is:
[Fact]
public void Test()
{
var sut = AutoMocking.Container.Resolve<SystemUnderTest>();
}This saves time to manually injecting mocked dependencies leaving you more time to focus on test themselves. Tests like this also become easily maintainable. Furthermore, Tethos will make sure to scan you test project references to load proper assemblies into the container.
To resolve mock to unit test simply resolve Mocking type and it will be resolved automatically.
In this example Moq is used:
[Fact]
public void Test_Exercise()
{
// Arrange
var mock = Container.Resolve<Mock<IMockable>>();
mock.Setup(m => m.Get())
.Returns(expected);
...
}within the scope of the test method dependencies, including mock instances will be exactly the same.
- Use
AutoMocking.Containerstatic property to retrieve container
public class ContainerFromBaseClass
{
[Fact]
public void Exercise_ShouldReturn42()
{
var sut = AutoMockingTest.Container.Resolve<SystemUnderTest>();
...
}
}- Inherit from
AutoMockingTestto have access toContainerproperty.
public class ContainerFromBaseClass: AutoMockingTest
{
[Fact]
public void Exercise_ShouldReturn42()
{
var sut = this.Container.Resolve<SystemUnderTest>();
...
}
}- Inject or initialize
IAutoMoqContainerdependency using static factory method:AutoMocking.Create().
public class ContainerAsProperty: AutoMockingTest
{
public IAutoMoqContainer Container { get; }
public ContainerAsProperty()
{
Container = AutoMocking.Create();
}
[Fact]
public void Exercise_ShouldReturn42()
{
var sut = this.Container.Resolve<SystemUnderTest>();
...
}
}Tethos scans project's output assemblies, registers them to IoC container and install mocking interceptor.
Assemblies are selected according to prefix in the name. I.e, if you test assembly is named Project.Tests, then Tethos will load every single Project.* assembly into auto-mocking container.
Else, Tethos will load project referenced assemblies into container.
Every single incoming dependency will be mocked if such dependency is an interface.
For example this dependency is will resolved without any issues.
public SystemUnderTest(IDependency dependency, ISubDependency subDependency)
{
...
}while here
public SystemUnderTest(int dependency, string subDependency)
{
...
}here, you will need to tell container what to resolve.
var sut = Container.Resolve<SystemUnderTest>(
new Arguments()
.AddTyped(42)
.AddTyped("foo")
);in case there multiple dependencies with same or different types
public SystemUnderTest(int minValue, int maxValue)
{
...
}You can use AddDependencyTo extension method to add dependency to certain parameter in the constructor.
var sut = this.Container.Resolve<SystemUnderTest>(
new Arguments()
.AddDependencyTo<Concrete, int>(nameof(minValue), minValue)
.AddDependencyTo<Concrete, int>(nameof(maxValue), maxValue));
);There are use cases when you can need to resolve parent dependency first before you can get to auto-mocked dependency. For this case, Tethos has ResolveFrom<TParent, TChild> extension method which will basically resolve mocked dependency in one go.
// Arrange
var mock = this.Container.ResolveFrom<SystemUnderTest, IMockable>();vs.
// Arrange
_ = this.Container.Resolve<SystemUnderTest>();
var mock = this.Container.Resolve<IMockable>();You can find demo projects code in /demo folder. There are examples using Tethos libraries with:
- xUnit
- NUnit
- MSTest
testing frameworks.
Internal dependencies can loaded into auto-mocking container. But due to possible performance caveats it's disabled by default. Check out Configuration section to figure our how to enable it.
Tethos can behavior be configured using AutoMockingConfiguration class instance.
| Item | Description | Default value |
|---|---|---|
| IncludeNonPublicTypes | Enables internal types to be loaded into auto-mocking container | False |
Since AutoMockingConfiguration is virtual you can override in the child class:
public class Test : AutoMockingTest
{
public override AutoMockingConfiguration AutoMockingConfiguration => new() { IncludeNonPublicTypes = false };
}alternatively, you can override OnConfigurationCreated method which allow you can edit configuration instance directly.
public class Test : AutoMockingTest
{
public override AutoMockingConfiguration OnConfigurationCreated(AutoMockingConfiguration configuration)
{
configuration.IncludeNonPublicTypes = true;
return base.OnConfigurationCreated(configuration);
}
}Tethos uses Moq to auto-mock incoming dependencies. Mocks can resolved using Mock<> wrapper type.
[Fact]
public void Test()
{
var mock = Container.Resolve<Mock<IMockable>>();
mock.Setup(m => m.Get()).Returns(42);
}alternatively, Moq's proxy objects can be resolved as well
[Fact]
public void Test()
{
var mock = Container.Resolve<IMockable>();
Mock.Get(mock).Setup(m => m.Get()).Returns(42);
}Tethos uses NSubstitute to auto-mock incoming dependencies. Since NSubstitute alter object direct, mock can resolve using direct types.
[Fact]
public void Test()
{
var mock = Container.Resolve<IMockable>(); // <-- This will be mocked
mock.Get().Returns(42);
}Tethos uses FakeItEasy to auto-mock incoming dependencies. FakeItEasy also doesn't wrap mocked object, same here:
[Fact]
public void Test()
{
var mock = Container.Resolve<IMockable>(); // <-- This will be mocked
A.CallTo(() => mock.Get()).Returns(42)
}