Модульный тест по модулю Autofac для достижения 100% покрытия кода
у нас есть основная библиотека, которая делает сложные вычисления, и мы считаем ее критической, и мы хотим иметь покрытие кода 100% на этой библиотеке. Теперь у нас есть 96%, что здорово, но мы не можем получить 100% из-за этого класса:
public class IoCModule : Autofac.Module
{
protected override void Load(Autofac.ContainerBuilder builder)
{
builder.RegisterType<SomeMathServiceA>().As<ISomeMathServiceA>();
builder.RegisterType<SomeMathServiceB>().As<ISomeMathServiceB>();
//... more registrations
}
}
Я не знаю, как проверить его, или если нам действительно нужно проверить его.
Я пробовал модульный тест, который принимает этот модуль и создает IContainer
и разрешает все зависимости регистра, но некоторые службы обращаются к файлам DB и config которые очень сложно высмеивать в этом контексте.
2 ответов
отказ от ответственности: модульный тест уровня класса не считается разумным
(автора)
Я думаю, под модульным тестированием вы подразумеваете "модульные тесты уровня класса", где блок является классом. Если вы хотите проверить IoCModule
вы должны использовать тестирование уровня компонента / библиотеки, где вы проверяете всю библиотеку, работает ли она правильно. Это (должно) включать IoCModule
- и все остальное в библиотеке. Обычно невозможно достичь 100% - ного охвата филиалов с помощью тестов на этом уровне, но комбинация тестов на этом уровне + модульные тесты уровня класса делают для очень хорошей надежности теста.
Я бы также сказал, что лучше достичь 80% комбинированного покрытия, чем иметь только модульные тесты уровня класса. Хотя каждый класс может работать точно в соответствии с тестом, все может работать не так, как предполагалось. Вот почему вы должны выполнять тесты на уровне компонентов.
Как проверить, зарегистрирован ли тип:
теперь, если вы все еще непреклонны выполняя тесты, не смотрите дальше, вы можете сделать это так:
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();
}
}
}
таким образом, каждый тип сообщается отдельно.
Вау, это было легко - так в чем проблема опять?
теперь в приведенном выше примере вы можете увидеть, что тест на single
(=float
) проходит. Теперь посмотрите на модуль:
public class MyModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<float>();
}
}
когда мы на самом деле пытаемся решить float
by:
container.Resolve<float>();
вот что происходит:
Autofac.Ядро.DependencyResolutionException: нет конструкторов для типа "система".Один "можно найти с помощью конструктора finder" Autofac.Ядро.Активаторы.Отображение.DefaultConstructorFinder'.
конечно, мы могли бы просто адаптировать тест для выполнения Resolve(Type t)
вместо IsRegistered(Type t)
- однако есть много других способов сделать тестовый проход, но реализация не удалась. Для пример:
- привязка типа для использования
builder.RegisterInstance<IFoo>(null)
- изменение продолжительности жизни / областей, поэтому он больше не работает должным образом.
Я, наконец, нашел способ проверить это. Модуль autofac имеет метод Configure, который регистрирует компоненты. Вот как я это сделал:--2-->
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;
}
}