Реализация смеющихся объектов с помощью Moq, когда конструктор имеет параметры - программирование

Реализация смеющихся объектов с помощью Moq, когда конструктор имеет параметры

Я прочитал этот ответ Раджицу по тому же вопросу. Я все еще запутался, как реализовать вещи. Может ли кто-нибудь дать мне пример реализации.

У меня есть следующие классы:

class Fizz : IFizz
{
}

class Buzz : IBuzz
{

}

class Bar : IBar
{

}

class Foo : IFoo
{
    public Foo(IBar bar, IFizz fizz, IBuzz buzz)
    {
        //initialize etc.
    }

    //public methods
}

Каков практический способ обойти конструктор здесь? Я хочу сделать что-то вроде

var foo = new Mock<IFoo>();

Другими словами, как бы код выглядел после совета

The best thing to do would be right click on your class and choose Extract interface.

4b9b3361

Ответ 1

Вы можете создать mock, где конструктор имеет аргументы параметров, ссылаясь на MockBehavior, показанный ниже

Mock<testClass>(MockBehavior.Strict, new object[] {"Hello"}); 

Ответ 2

TL; DR

Непонятно, что ты на самом деле пытаешься сделать.

Если вы уже определили интерфейс IFoo (который, как кажется, показывает ваш вопрос), и вам нужно смоделировать его для другого типа, вы уже настроены: var bazz = new Bazz(new Mock<IFoo>());

Если у вас есть конкретный класс Foo который вы не абстрагировали в интерфейс, который нужно предоставить другому типу в качестве зависимости, то извлечение интерфейса является одним из наиболее распространенных способов разрешить инициализацию объекта Bazz без создания фактический класс Foo. В зависимости от того, как выглядит ваш настоящий код, вы можете ограничить область видимости, абстрагируя в IFoo только те части Foo, от которых зависит Bazz. Другой способ - это смоделировать бетон и предоставить moq аргументы конструктора для использования. Вам нужно будет убедиться, что зависимые части Foo "Mock-способны" (общедоступная виртуальная), чтобы предоставить Mock ваши настройки и перезапуски.

Редактировать:

Как редактировать, чтобы действительно ответить на вопрос

как бы код следовал совету? Лучше всего было бы... Извлечь интерфейс?

Совет по извлечению интерфейса может быть не совсем тем, что вы ищете. Как уже упоминалось в других ответах, мы не уверены, что именно вы на самом деле пытаетесь проверить.

Если вы пытаетесь проверить функциональность на бетонном Foo реализации IFoo затем извлекая интерфейс и насмешливый IFoo самом деле не собирается, чтобы помочь вам, потому что собирается MOQ реализовать интерфейс сам по себе и только обеспечивают "функциональность" вы сказать это предоставить в настройках методы и свойства этой реализации. Если это то, что вы делаете, И тестируемый метод вызывает какой-то другой метод в конкретной реализации Foo, И этот метод - то, для чего вы хотите предоставить абстракцию, тогда я бы сделал, как упомянул @Chris Marisic, и изменил метод, который вы используете. желание абстрагироваться до virtual. Используя это как виртуальное, вы можете использовать макет для предоставления абстракции или сделать то же, что я делал в прошлом, и создать подтип для тестируемого предмета в тестовом классе.

public class MyFooTests
{
    ... // Tests
    private class SubFoo : Foo
    {
        public override void MethodToAbstract(bool par)
        {
            // do something expected
        }
    }
}

С этим методом вам все еще нужно будет предоставить что-то конструктору Foo, но это было бы хорошим примером для использования mock на этих интерфейсах.

Теперь, если вы тестируете Foo а тестируемый метод просто вызывает методы на IBar, IFizz, IBuzz, и нет другой абстракции, которую вы хотите создать, то вы уже настроены. Смоделируйте эти интерфейсы, настройте их так, чтобы они возвращали то, что вы ожидаете в конкретном тестовом примере, и предоставьте Foo фиктивные объекты.

Советы по извлечению интерфейса действительно полезны, когда тестируемый объект зависит от Foo, как упомянул @Chris Marisic. Тем не менее, я не могу разделить его мнение о нежелании создавать интерфейсы в этой ситуации. Это зависит от того, что пахнет для вас больше: создание интерфейса для предоставления имитации или изменение модификатора для обеспечения того же. Если вы собираетесь изменить модификатор и все еще использовать moq для макетирования конкретного Foo, вам нужно будет либо предоставить конструктор по умолчанию (довольно вонючий для меня), либо создать макет с заданной спецификацией аргументов конструктора, как упомянуто в вашей ссылке ответ.

Допустим, у вас есть следующее:

public class Baz
{
    private Foo foo;

    public Baz(Foo inputFoo)
    {
        this.foo = inputFoo;
    }

    public bool GetFromTheFoo()
    {
        // other stuff here
        var fooStuff = this.foo.GetStuff();
        // logic on the stuff;
        return fooStuff != null;
    }
}

В приведенном выше Baz зависит от Foo. После извлечения интерфейса Foo Baz должен будет принять IFoo.

Тогда Foo будет выглядеть так, как в вашем вопросе, а IFoo будет иметь определение сигнатуры для метода, который вы хотите абстрагировать от Baz.

object GetStuff();

Теперь в вашем тесте вы все равно будете использовать var foo = new Mock<IFoo>(); и дайте foo для вашего подопытного Baz. Не забудьте настроить метод IFoo.GetStuff().

foo.Setup(f => f.GetStuff()).Returns(new object());

Поскольку вы уже создали абстракцию IFoo, нет необходимости

обойти конструктор здесь?

потому что макет IFoo имеет только конструктор по умолчанию, предоставленный moq.

Лично я предпочитаю интерфейсный подход, когда он применяется, потому что он удаляет IFizz, IBuzz и IBar из цепочки зависимостей на Baz. Если Baz зависит от Foo а Foo зависит от IFizz etc., IFizz etc. Baz также зависит от IFizz etc. После извлечения IFoo Baz зависит только от IFoo.


Оригинальный ответ (не фактический ответ #chagrin)

Я знаю, что это было задано некоторое время назад, и, возможно, для меня не имеет смысла добавлять это сейчас, но для будущих читателей, давайте не зацикливаться на мысли, что при использовании moq то же самое:

var fooDouble = new Mock<IFoo>();

это не то же самое, что:

var fooDouble = new Mock<Foo>();

Насколько я понимаю, при создании Mock of IFoo, среда moq собирается сгенерировать двойной класс, который реализует интерфейс IFoo, обеспечивая таким образом конструктор по умолчанию для себя в сгенерированной реализации. Однако макет собственно Foo не реализует интерфейс непосредственно в сгенерированном коде, а скорее наследует от конкретного класса Foo и, следовательно, требует, чтобы любые методы, которые должны быть заглушены, стали виртуальными, и если есть какие-то конкретные реализации, которые вы хотите чтобы на самом деле использовать (например, тестируемый модуль), тогда вам понадобится "CallBase = true" на макете. Если это то, что вы хотите сделать с конкретным классом, имеющим аргументы конструктора, возможно, вы ищете ответ @Palkin, чтобы не показывать конструктор по умолчанию.

Ответ 3

Вы не должны ничего менять, если у вас есть интерфейс IFoo и хотите издеваться над классом Foo, у которого есть конструктор с параметрами.

Ваш код - именно то, что вам нужно.

var foo = new Mock<IFoo>();

Следующий совет охватывает ситуации, когда класс не имеет интерфейса и без конструктора без параметров. Фактически вы можете передать все необходимые параметры в конструктор Mock.

var mock = new Mock<IFoo>("constructor", "arguments");

но

"Лучшее, что нужно сделать, - это щелкнуть правой кнопкой мыши по вашему классу и выбрать" Извлечь интерфейс ". (С)

Ответ 4

Лучшее, что вам нужно сделать, это щелкнуть правой кнопкой мыши по вашему классу и выбрать "Извлечь интерфейс".

Я собираюсь рассмотреть эту концепцию в ортогональном смысле. Я не согласен с этим утверждением, интерфейс не является решением для рассматриваемой situtation.

Возвращаясь к предыдущему тексту вопроса:

public class CustomerSyncEngine {
    public CustomerSyncEngine(ILoggingProvider loggingProvider, 
                              ICrmProvider crmProvider, 
                              ICacheProvider cacheProvider) { ... }

    public void MethodWithDependencies() {
        loggingProvider.Log();
        crmProvider.Crm();
        cacheProvider.Cache();
    }
}

Обратите внимание на метод, который я добавил.

Я считаю, что реальный вопрос заключается в том, что вы специально не тестируете CustomerSyncEngine, а вместо этого проверяете класс, который зависит от CustomerSyncEngine. Позвольте назвать этот класс SuperSyncEngine. Создание теста против SuperSyncEngine будет больно, так как вы должны издеваться над всем CustomerSyncEngine с его тремя интерфейсами вместе с любыми другими дополнительными зависимостями SuperSyncEngine.

Учитывая, что код, который вы пытаетесь проверить, SuperSyncEngine, который зависит от CustomerSyncEngine, здесь не является интерфейсом. Вы можете создать ICustomerSyncEngine, но этот интерфейс не должен создаваться просто для насмешливой структуры. Лучшее решение - изменить CustomerSyncEngine.MethodWithDependencies на виртуальный

public virtual void MethodWithDependencies() {
    loggingProvider.Log();
    crmProvider.Crm();
    cacheProvider.Cache();
}

Это позволит вам заменить метод на насмешливую структуру, игнорируя зависимости, с которыми поставляется CustomerSyncEngine.

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

Ответ 5

Это своего рода старый пост, но я столкнулся с аналогичной проблемой (начиная с Moq). Если у кого-то есть аналогичная проблема, вот что я имел:

class Bar : IBar
{
}

class Foo : IFoo
{
    public Foo(IBar bar)
    {
        //initialize etc.
    }

    //public methods
}

class Manager : IManager
{
    public Manager(Foo foo)
    {
        //initialize etc
    }
}

То, что я пытаюсь сделать, это тест Manager not Foo.

Вот мой первоначальный тестовый код, в котором была ошибка.

[TestFixture]
public class ManagerTest
{
    [Test]
    public void SomeTest()
    {
        var fooMock = Mock<IFoo>();
        var managerUnderTest = new Manager(fooMock.Object);
    }
}

Ошибка Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Something.Models.Foo. Could not find a parameterless constructor.

Считывая сообщение об ошибке, Moq не понимает, как создать экземпляр Foo, поскольку нет конструктора без параметров, и мы не укажем Moq, как создать экземпляр с параметрами. Измените этот второй раздел на:

[TestFixture]
public class ManagerTest
{
    [Test]
    public void SomeTest()
    {
        var barMock = Mock<IBar>();
        var fooMock = Mock<IFoo>(barMock.Object);
        var managerUnderTest = new Manager(fooMock.Object);

        //proceed with test
    }
}

Ответ 6

Если вы перейдете к тестированию своего класса FOo, вам не нужно издеваться. Вам нужно только высмеять те классы, которые зависят от класса, который вы пытаетесь проверить.

Что-то вроде:

Mock<IBar> bar = new Mock<IBar>();
Mock<IBuzz> buzz = new Mock<IBuzz>();
Mock<IFizz> fizz= new Mock<IFizz>();

Foo foo = new Foo(bar.Object, buzz.Object, fizz.Object);

Затем вызовите метод в foo, который вы хотите проверить;)

Если метод в foo использует какой-либо метод внутри bar/fuzz или fizz, тогда вы должны использовать sintax, например:

buzz.Setup(x => x.DoSomething()).Returns(1);

Таким образом, когда вы вызываете метод foo, он будет вызывать DoSomething и всегда будет возвращать 1;)

Ответ 7

Интерфейсы полезны, когда один тип имеет более одной реализации. С отрицательной стороны это означает, что вы должны заботиться о том, чтобы не выводить интерфейсы только потому, что вам нужны интерфейсы (общая ошибка). С положительной стороны, издевательский интерфейс является второй реализацией по определению.

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

В соответствующей заметке при определении интерфейса обязательно добавьте функциональные части в интерфейс: методы, которые определяют, что делает объект, а не то, что он выглядит. Наличие интерфейса с кучей геттеров/сеттеров не придает значения дизайну. Это довольно большая область теории, и это крошечное окно не для того, чтобы больше писать об этом.

Чтобы прояснить связь с вашим вопросом: вымытая реализация должна обеспечивать поведение, требуемое интерфейсом. Для этого вы используете функции издевательской структуры. Это не имеет никакого отношения к конкретной реализации класса Foo - вы определяете конкретное поведение объекта Mock.