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

1

У меня есть следующий сценарий:

public void DoSomething(...)
{
   ...
   ClassA obj1 = new ClassA();
   ClassB obj2 = new ClassB();
   ClassC obj3 = new ClassC();
   ...
}

Я понимаю, что если бы я использовал принцип инверсии зависимостей, я мог бы размахивать интерфейсом, и он будет работать. Проблема в том, что эти экземпляры необходимы только в этом методе, поэтому нет смысла создавать их на уровне класса. Кроме того, некоторые методы имеют 5-6 объявлений объектов, поэтому передача этих параметров будет раздувать список параметров.

Есть ли в любом случае я могу использовать Moq, NMock и т.д. Для моделирования этих классов (которые основаны на общем интерфейсе)?

Теги:
unit-testing
moq
nmock

2 ответа

2

Если вам нужно только создать эти типы в этом методе, вы правы в том, что их не следует вводить в класс.

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

Здесь обычный подход:

interface IMessageFactory
{
    IMessage CreateMessage(string text);   
}

class ConcreteMessageFactory : IMessageFactory
{
    IMessage CreateMessage(string text) { return new Message(text); }
}

class MockMessageFactory : IMessageFactory
{
    public IMessage CreateMessage(string text) { return new MockMessage(text); }
}


class MyClient
{
    private readonly IMessageFactory _msgFactory;

    public MyClient(IMessageFactory msgFactory)
    {
        _msgFactory = msgFactory;   
    }

    public void SendMessage(string text)
    {
        IMessage msg = _msgFactory.CreateMessage(text);
        msg.Send();
    }
}


//Usage:
new MyClient(new ConcreteMessageFactory());
//Mocking
new MyClient(new MockMessageFactory());

В зависимости от того, какой каркас DI вы используете, инфраструктура может сделать этот способ более удобным для вас. Например, Castle Windsor позволяет вам вводить фабрику на основе делегата, в отличие от фабрики на основе интерфейса:

class MyClient
{
    private readonly Func<string, IMessage> _msgFactory;

    public MyClient(Func<string, IMessage> msgFactory)
    {
        _msgFactory = msgFactory;   
    }

    public void SendMessage(string text)
    {
        IMessage msg = _msgFactory(text);
        msg.Send();
    }
}

//Usage:
new MyClient(() => new Message());
//Mocking
new MyClient(() => new MockMessage());
1

Вы можете использовать что-то вроде StructureMap для выполнения вашей работы, ваш метод станет чем-то вроде.

public void DoSomething(...)
{
   ...
   IClassA obj1 = ObjectFactory.GetInstance<IClassA>();
   IClassB obj2 = ObjectFactory.GetInstance<IClassB>();
   IClassC obj3 = ObjectFactory.GetInstance<IClassC>();
   ...
}

Тогда, как правило, вы должны сопоставлять их как в global.asax, так и в аналогичных

        ObjectFactory.Initialize(x =>
        {
            x.ForRequestedType<IClassA>().TheDefaultIsConcreteType<ClassA>();
            ...etc
        });

Но для ваших модульных тестов

        ObjectFactory.Initialize(x =>
        {
            x.ForRequestedType<IClassA>().TheDefaultIsConcreteType<UnitTestClassA>();
            ...etc
        });

Ещё вопросы

Сообщество Overcoder
Наверх
Меню