We have a core library which make complex calculations and we consider it critical and we want to have a code coverage of 100% on that library. We now have 96% which is great, but we cant get the 100% because of this class:
public class IoCModule : Autofac.Module
{
protected override void Load(Autofac.ContainerBuilder builder)
{
builder.RegisterType<SomeMathServiceA>().As<ISomeMathServiceA>();
builder.RegisterType<SomeMathServiceB>().As<ISomeMathServiceB>();
//... more registrations
}
}
I dont know how to test it, or if we really need to test it.
I have tried a unit test that take this module and create an IContainer
and resolves every register dependency but some services access DB and config files which are very complex to mock in this context.
Done!!!!
With that being said it is generally accepted that 80% coverage is a good goal to aim for. Trying to reach a higher coverage might turn out to be costly, while not necessary producing enough benefit. The first time you run your coverage tool you might find that you have a fairly low percentage of coverage.
(by the author)
I guess by unit-testing you mean "class level unit tests" where the unit is a class. If you want to test the IoCModule
you should employ component/library level testing where you test the whole library whether it's working correctly. This (should) include the IoCModule
- and all the other stuff in the library. It's usually not feasible to reach 100% branch coverage using tests on this level, but the combination of tests on this level + class level unit tests make for a very good test reliability.
I'd also say it's better to reach 80% combined coverage than to only have class-level unit tests. While every class itself can work exactly according to the test, the whole may not work as intended. That's why you should perform component-level tests.
Now if you're still adamant about performing the tests, look no further, you can do it like that:
public class MyModuleTest
{
private IContainer container;
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
var containerBuilder = new ContainerBuilder();
// register module to test
containerBuilder.RegisterModule<MyModule>();
// don't start startable components -
// we don't need them to start for the unit test
this.container = containerBuilder.Build(
ContainerBuildOptions.IgnoreStartableComponents);
}
[TestCaseSource(typeof(TypesExpectedToBeRegisteredTestCaseSource))]
public void ShouldHaveRegistered(Type type)
{
this.container.IsRegistered(type).Should().BeTrue();
}
[TestFixtureTearDown]
public void TestFixtureTearDown()
{
this.container.Dispose();
}
private class TypesExpectedToBeRegisteredTestCaseSource : IEnumerable<object[]>
{
private IEnumerable<Type> Types()
{
// whatever types you're registering..
yield return typeof(string);
yield return typeof(int);
yield return typeof(float);
}
public IEnumerator<object[]> GetEnumerator()
{
return this.Types()
.Select(type => new object[] { type })
.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
This gives test output like:
so each type is reported separately.
Now in the above example you can see that the test for single
(=float
) is passing. Now look at the module:
public class MyModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<float>();
}
}
when we actually try to resolve float
by:
container.Resolve<float>();
this is what happens:
Autofac.Core.DependencyResolutionException : No constructors on type 'System.Single' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'.
Of course we could just adapt the test to perform the Resolve(Type t)
instead of using IsRegistered(Type t)
- however there's plenty of other ways to make the test pass - but the implementation fail. For example:
builder.RegisterInstance<IFoo>(null)
I finally found a way to test it. The autofac Module has a method Configure that register the components. This is how I did it:
public class CheckRegistrations
{
[Test]
public void Should_Have_Register_Types()
{
//Arrange
var typesToCheck = new List<Type>
{
typeof (ISomeMathServiceA),
typeof (ISomeMathServiceB)
};
//Act
var typesRegistered = this.GetTypesRegisteredInModule(new IoCModule());
//Arrange
Assert.AreEqual(typesToCheck.Count, typesRegistered.Count());
foreach (var typeToCheck in typesToCheck)
{
Assert.IsTrue(typesRegistered.Any(x => x == typeToCheck), typeToCheck.Name + " was not found in module");
}
}
private IEnumerable<Type> GetTypesRegisteredInModule(Module module)
{
IComponentRegistry componentRegistry = new ComponentRegistry();
module.Configure(componentRegistry);
var typesRegistered =
componentRegistry.Registrations.SelectMany(x => x.Services)
.Cast<TypedService>()
.Select(x => x.ServiceType);
return typesRegistered;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With