Шаблоны создания тестируемой системы TDD (AutoFixture) - PullRequest
3 голосов
/ 01 июля 2010

Я пытаюсь использовать SUT Factory 'pattern' для создания моего SUT.

Учитывая структуру SUT:

namespace MySut
{
    public class Dep1
    {
    }

    public class Dep2
    {
    }

    public class Sut
    {
        public Sut( Dep1 dep1, Dep2 dep2 )
        {
        }
    }
}

Я использую AutoFixture , и мне интересно, как лучше всего свернуть следующие спецификации и связанный с ними заводской метод SUT [ценный, но] занятая работа:

namespace MySpecifications
{
    using MySut;
    public class MySpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = CreateSut();
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = CreateSut( new Mock<Dep1>().Object );
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = CreateSut( new Mock<Dep2>().Object );
        }

        private object CreateSut( )
        {
            return CreateSut( CreateDep1(), CreateDep2() );
        }

        private object CreateSut( Dep1 dep1 )
        {
            return CreateSut( dep1, CreateDep2() );
        }

        private object CreateSut( Dep2 dep2 )
        {
            return CreateSut( CreateDep1(), dep2 );
        }

        private Sut CreateSut( Dep1 dep1, Dep2 dep2 )
        {
            return new Sut( dep1, dep2 );
        }

        private static Dep1 CreateDep1()
        {
            return new Fixture().CreateAnonymous<Dep1>();
        }

        private static Dep2 CreateDep2()
        {
            return new Fixture().CreateAnonymous<Dep2>();
        }
    }
}

примерно так:

    public class MyAutoFixturedSpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = CreateSut();
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = CreateSut( new Mock<Dep1>().Object );
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = CreateSut( new Mock<Dep2>().Object );
        }

        private object CreateSut( params object[] injectedNonAnonymouses )
        {
            return new Fixture(  ).Build<Sut>(  )./*??????*/;
        }
    }

или:

    public class MyAnticipatedAutoFixturedSpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = new Fixture(  ).Build<Sut>().CreateAnonymous(  );
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = new Fixture().Build<Sut>()/*.With( new Mock<Dep1>().Object )*/.CreateAnonymous();
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = new Fixture().Build<Sut>()/*.With( new Mock<Dep2>().Object )*/.CreateAnonymous();
        }
    }

т. Е. Удаление всего заводского мусора, так что мои спецификации могут легко справиться с переходом к:

namespace MySutWithNewDependency
{
    public class Dep1
    {
    }

    public class Dep2
    {
    }

    public class Dep3
    {
    }

    public class Sut
    {
        public Sut( Dep1 dep1, Dep2 dep2, Dep3 dep3 )
        {
        }
    }
}

Хотя есть совпадение с понятиемКонтейнер с автоматической блокировкой, я до сих пор не ищу золотого молотка - просто способ, позволяющий имитировать 0 или 1 вещь за раз, и в то же время иметь возможность настраивать создание зависимых объектов без необходимости повторного обращения к явным вызовам конструктора Sutвсякий раз, когда изменяется его набор зависимостей.

(также с использованием xUnit.net (стиль SubSpec), Moq, Ninject2 (хотя я не хочу использовать DI в моих спецификациях))

1 Ответ

4 голосов
/ 07 июля 2010

AutoFixture может более или менее сделать все это для вас без всех этих дополнительных лесов. Я бы начал с базового прибора и использовал его для разрешения Sut:

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

Предполагается, что Dep1 и Dep2 могут быть созданы автоматически с помощью AutoFixture, но, как представлено, они могут, потому что они имеют конструкторы по умолчанию.

Если вы хотите переопределить определенный тип, вы можете использовать метод Register следующим образом:

var fixture = new Fixture();

var mock = new Mock<Dep1>();
fixture.Register(mock.Object);
// Setup mock if necessary...

var sut = fixture.CreateAnonymous<Sut>();

Это заставит fixture использовать mock.Object в любое время, когда потребуется Dep1, в том числе при вызове конструктора Sut. Это определенно соответствует вашему требованию быть надёжным перед лицом развивающихся конструкторов, и одна из главных причин, почему AutoFixture была построена таким образом.

В более реалистичном сценарии Dep1 и Dep2 могут быть интерфейсами, и в этом случае вы можете зарегистрировать их как часть «Fixture по умолчанию». Это, безусловно, также возможно, потому что последний звонок в Регистр выигрывает . Это означает, что вы можете настроить экземпляр Fixture с некоторыми хорошими значениями по умолчанию и при этом иметь возможность переопределять определенные типы всякий раз, когда вам нужно это сделать.

Лично я использую AutoFixture в качестве контейнера для автоматической проверки. Это обсуждение дает подсказку о том, как это было бы возможно с помощью AutoFixture 1.1 API, но новое ядро ​​для AutoFixture 2.0 предоставит гораздо лучшие варианты расширяемости. Когда я доберусь до этого, я напишу в блоге на эту тему.

P.S. Извините за поздний ответ, но я не видел ваш вопрос до сих пор. В будущем не стесняйтесь пинговать меня (например, в Twitter) для более быстрого ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...