Как я могу проверить класс, который берет фабрику? - PullRequest
4 голосов
/ 23 марта 2012

Я пытаюсь протестировать класс, который принимает фабрику (Func<T>), и я использую Moq и AutoFixture.

Каков наилучший способ настроить «среду», чтобы увидеть, использовалась ли фабрика и сколько раз и какие методы использовались в возвращаемых экземплярах?

В настоящее время яПодшучивает над T и Injecting a Func<T>, который ведет подсчет всех возвращенных экземпляров Mock:

public class SystemUnderTest {
    public SystemUnderTest(Func<IMyClass> c)
    {
        try {
            var c1 = c();
            c1.Name="n1";
            c1.Send();
        }
        catch(Exception){
            var c2 = c();
            c2.Name="n2";
            c2.Send();
        }
    }
}
private Mock<IMyClass> MockFactory()
{
   var m = new Mock<IMyClass>();
   m.SetupProperty(mc=>mc.Name);
   _returnedStubs.Add(m);
   return m;
}  
[Test]
public void TestSomething()
{
    var fixture = new Fixture();
    fixture.Inject(()=>MockFactory().Object)
    var sut = fixture.CreateAnonymous<SystemUnderTest>();
    Assert.That(_returnedStubs.Count,Is.Equal(1));
    _returnedStubs[0].Verify(m=>m.Send(),Times.Exactly(1));
    _returnedStubs[0].Verify(m=>m.Name = "n1");
}

Но мне это кажется немного странным / уродливым.И я почти уверен, что переменная экземпляра в тестовом классе - опасная вещь

Ответы [ 2 ]

7 голосов
/ 24 марта 2012

Поскольку AutoFixture может создавать анонимные делегаты , при запросе на создание анонимного экземпляра SystemUnderTest он также автоматически предоставляет анонимный делегат Func<IMyClass>, который, в свою очередь, возвращает анонимный экземплярIMyClass при вызове.

Это означает, что для данного сценария:

public class SystemUnderTest
{
    public SystemUnderTest(Func<IMyClass> c)
    {
        try
        {
            var c1 = c();
            // ...
        }
        catch (Exception)
        {
            var c2 = c();
            // ...
        }
    }
}

следующий код:

var fixture = new Fixture();
var sut = fixture.CreateAnonymous<SystemUnderTest>();

назначит c1 и c2 переменных с анонимными экземплярами IMyClass.Кроме того, если вы сконфигурируете AutoFixture так, чтобы работал как контейнер для автоматической имитации , например, с использованием AutoMoqCustomization, эти анонимные экземпляры IMyClass также будут являться прокси Moq:

var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization());
var sut = fixture.CreateAnonymous<SystemUnderTest>();

Однако эта информация, хотя и полезная, на самом деле не помогает вам в вашем конкретном случае, так как вам нужно овладеть ложными объектами , возвращаемыми фабрикой Func<IMyClass>метод в вашем тесте, чтобы настроить их поведение и сделать некоторые утверждения о том, как с ними взаимодействовали.

На мой взгляд, лучшее решение - изменить реализацию фабричного метода от Func<IMyClass> до интерфейс .Таким образом, вы можете создать фальшивую фабрику, которая возвращает различные макеты интерфейса IMyClass, когда метод Create вызывается несколько раз в последовательности :

Итак, учитывая этот пример:

public interface IFactory<T>
{
    T Create();
}

public class SystemUnderTest
{
    public SystemUnderTest(IFactory<IMyClass> factory)
    {
        try
        {
            var c1 = factory.Create();
            // ...
        }
        catch (Exception)
        {
            var c2 = factory.Create();
            // ...
        }
    }
}

Вы можете настроить свой тестовый сценарий следующим образом:

    // Given
    var c1 = new Mock<IMyClass>();
    var c2 = new Mock<IMyClass>();
    // TODO: setup the behavior of the mock objects
    var factory = new Mock<IFactory<IMyClass>>();
    factory.Setup(s => s.Create()).ReturnsInOrder(c1.Object, c2.Object);

    // When
    var fixture = new Fixture();
    fixture.Inject(() => factory.Object)
    var sut = fixture.CreateAnonymous<SystemUnderTest>();

    // Then
    // TODO: verify the expectations on the mock objects

Обратите внимание, что ReturnsInOrder - это пользовательский метод расширения который использует метод Callback в Moq для возврата различных значений из метода-заглушки, когда он вызывается несколько раз подряд.

1 голос
/ 23 марта 2012

Один из лучших способов - создать собственную функцию и передать ее, а затем настроить ожидания или указать там.

int numberOfTimesUsed = 0;
myObject.Func = (x) => 
{ 
   numberOfTimesUsed++;
   Assert.IsNotNull(x); // checks if x passed was not null
};

...

Assert.AreEqual(2, numberOfTimesUsed); // checks if called twice
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...