StructureMap: Как я могу провести модульное тестирование класса реестра? - PullRequest
8 голосов
/ 18 марта 2010

У меня есть класс реестра, подобный этому:

public class StructureMapRegistry : Registry
{
    public StructureMapRegistry()
    {
        For<IDateTimeProvider>().Singleton().Use<DateTimeProviderReturningDateTimeNow>();
    }

Я хочу проверить, что конфигурация соответствует моим намерениям, поэтому я начинаю писать тест:

public class WhenConfiguringIOCContainer : Scenario
{
    private TfsTimeMachine.Domain.StructureMapRegistry registry;
    private Container container;

    protected override void Given()
    {
        registry = new TfsTimeMachine.Domain.StructureMapRegistry();
        container = new Container();
    }

    protected override void When()
    {
        container.Configure(i => i.AddRegistry(registry));
    }

    [Then]
    public void DateTimeProviderIsRegisteredAsSingleton()
    {
        // I want to say "verify that the container contains the expected type and that the expected type
        // is registered as a singleton
    }
}

Как проверить, соответствует ли реестр моим ожиданиям? Примечание: я представил контейнер, потому что я не видел каких-либо методов проверки, доступных в классе Registry. В идеале я хочу проверить класс реестра напрямую.

Ответы [ 3 ]

3 голосов
/ 17 октября 2012

В StructureMap реестр используется для генерации PluginGraph; поэтому для модульного тестирования реестра необходимо убедиться, что его дизайн выдает правильный график. К сожалению, тестовая проверка лучше всего проводится по внутреннему свойству, вот пример:

public interface IFoo {}

public class SomeFoo : IFoo {}

public class FooRegistry : Registry
{
  public FooRegistry()
  {
    For<IFoo>().Use<SomeFoo>();
  }
}

[TestFixture]
public class FooRegistryTests
{
  [Test]
  public void ForIFoo_UseSomeFoo_AsDefaultInstance()
  {
    // Arrange
    var registry = new FooRegistry();

    // Act
    var pluginGraph = registry.Build();
    var iFooPluginFamily = pluginGraph.FindFamily(typeof(IFoo));
    var defaultInstance = iFooPluginFamily.GetDefaultInstance();

    // Assert
    Assert.IsTrue(defaultInstance.UsesConcreteType<SomeFoo>());
  }
}

public static class TestExtensions
{
  public static bool UsesConcreteType<T>(this Instance instance)
  {
    var concreteTypeProperty = typeof (Instance).GetProperty("ConcreteType", BindingFlags.Instance | BindingFlags.NonPublic);
    if (concreteTypeProperty == null || concreteTypeProperty.PropertyType != typeof(Type))
    {
      Assert.Inconclusive("Unable to locate the internal StructureMap.Instance.ConcreteType property");
    }
    var propertyValue = concreteTypeProperty.GetValue(instance, new object[] {}) as Type;

    return typeof (T) == propertyValue;
  }
}

Тестирование внутреннего свойства никогда не желательно, но в случае тестирования реестра это был лучший подход, который я нашел. Метод расширения пытается быть достаточно умным, чтобы иметь возможность делать тесты, которые полагаются на него, неубедительными при изменении внутреннего API.

3 голосов
/ 21 марта 2010

Думайте о классе реестра как о файле конфигурации - на самом деле нет смысла тестировать его изолированно, но вы можете проверить, как другой класс реагирует на него. В этом случае вы протестируете поведение контейнера при регистрации в реестре, поэтому вы были на правильном пути, введя контейнер в свой тест.

В своем тесте вы можете запросить IDateTimeProvider и подтвердить, что возвращаемый конкретный тип соответствует ожидаемому. Вы также можете извлечь 2 экземпляра из контейнера и утверждать, что они являются одним и тем же экземпляром (ReferenceEquals) для проверки поведения синглтона.

1 голос
/ 06 октября 2014

Отметьте этот интересный файл http://lostechies.com/jimmybogard/2010/01/22/advanced-structuremap-diagnosing-problems/, например:

[Test]
public void Should_connect_delete_handler_by_registry() 
{
     var container = new Container(new HandlerRegistry());
     var handler = container.GetInstance<IHandler<DeleteEntityCommand<Customer>>>();
     handler.ShouldBeInstanceOf<DeleteEntityCommandHandler<Customer>>(); 
}
...