есть ли лучший способ проверить этот метод?

Я хотел бы запустить некоторые модульные тесты для метода ниже. Я передаю макет интерфейса (vehicleObject) в ProcessVehicles, но как только он передается, он переназначается DetermineVehicleType, так что мой издевательский объект бесполезен. Моя первая идея состояла в том, чтобы создать логическое значение, чтобы определить, следует ли запускать DetermineVehicleType, и добавить его в качестве параметра, но это звучит очень запутанно. Есть ли лучший способ обойти это?

Метод с вводом Mock-объекта:

public void ProcessVehicles(ICarObject CarObject)
{
    IObject vehicleObject = DetermineVehicleType(carObject);
    vehicleObject.ProcessVehicle(carObject);
}

Оригинальный код:

public void ProcessVehicles()
{
    IObject vehicleObject = DetermineVehicleType(carObject);
    vehicleObject.ProcessVehicle(carObject);
}

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

метод DefineVehicleType является закрытым

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


person Micah Armantrout    schedule 21.03.2012    source источник
comment
Что вы хотите проверить, если вы издеваетесь над VehicleObject? Если ProcessVehicle вызывается или что-то в этом роде?   -  person Wouter de Kort♦    schedule 21.03.2012
comment
Можете ли вы дать подробное объяснение того, какова цель метода ProcessVehicles? Я чувствую здесь плохой запах кода; путем передачи объекта функции и изменения его ссылки на функцию, имеющую carObject в качестве параметра.   -  person daryal    schedule 21.03.2012
comment
@WouterdeKort Я хочу протестировать метод ProcessVehicles   -  person Micah Armantrout    schedule 21.03.2012
comment
@daryal Я отредактировал свой код так, как он был изначально, я не буду спорить, что есть неприятный запах кода, я могу изменить его, просто пытаясь найти лучший способ протестировать его.   -  person Micah Armantrout    schedule 21.03.2012
comment
Да, хорошо.. но что вы хотите проверить? Хотите убедиться, что DefinitionVehicleType возвращает правильное значение? Или что ProcessVehicle что-то делает? Можете ли вы показать свой тестовый код в стиле Arrange, Act, Assert?   -  person Wouter de Kort♦    schedule 21.03.2012
comment
На данный момент я просто хочу убедиться, что вызывается DefineVehicleType.   -  person Micah Armantrout    schedule 21.03.2012
comment
Похоже, я не получил вопроса, вы сказали, что I am passing a mocked interface(vehicleObject) into ProcessVehicles but as soon as it is passed it gets reassigned by DetermineVehicleType, но на самом деле вы передаете carObject в DetermineVehicleType(carObject), так что вы имеете в виду, что издевались над carObject?   -  person sll    schedule 22.03.2012
comment
carObject издевается, я отредактировал свой вопрос, чтобы он был более понятным   -  person Micah Armantrout    schedule 22.03.2012


Ответы (2)


Какой модификатор доступа имеет DetermineVehicleType? Вы можете заглушить этот метод, чтобы он возвращал ваш фиктивный интерфейс (Рой Ошеров, кажется, называет это abstract test driver pattern). В противном случае это выглядит как главный кандидат на рефакторинг :)

Чтобы реорганизовать свой код, вы должны сделать что-то вроде этого

Сначала измените подпись метода

protected virtual IObject DetermineVehicleType(CarObject obj)
{
    //Do whatever you normally do
}

Затем в своем тесте вы можете создать заглушку из вышеуказанного класса и заставить ее возвращать заглушенный IObject независимо от переданного CarObject. Вы можете либо вручную создать класс-заглушку, наследуя от класса, либо вы можете использовать что-то как MOQ, чтобы выполнить это. Дайте мне знать, если вам нужно, чтобы я подробнее остановился на этом.

Однако другое замечание:

Лучшим способом рефакторинга было бы просто передать IObject в ProcessVehicles, поскольку из этого примера видно, что у вас есть SRP, где метод ProcessVehicles делает больше, чем обрабатывает их. Но, может быть, это только из этого упрощенного примера

ПОЛНОЕ обновление реализации

    [Test]
    public void TestMethod()
    {
        var testerStub = new TesterStub();
        testerStub.ProcessVehicles();
        //Assert something here
    }

    public class TesterStub : Tester
    {
        public override IObject DetermineVehicleType(CarObject obj)
        {
            var mockObject = new Mock<IObject>();
            mockObject.Setup(x => x.SomeMethod).Returns(Something);
            return mockObject.Object;
        }
    }

    public class Tester
    {
        protected virtual IObject DetermineVehicleType(CarObject obj)
        {
            return new ObjectTester();
        }

        public void ProcessVehicles()
        {
            var carType = DetermineVehicleType(new CarObject());

        }
    }

    public class ObjectTester : IObject
    {
    }

    public interface IObject
    {
    }

    public class CarObject
    {
    }
person Justin Pihony    schedule 21.03.2012
comment
Обновлено, чтобы объяснить это немного больше. - person Justin Pihony; 21.03.2012
comment
Да, я согласен, что есть много нарушений. Я хочу протестировать проект, прежде чем я рефакторинг проекта. Не могли бы вы подробнее рассказать о своей идее рефакторинга? - person Micah Armantrout; 21.03.2012
comment
Обновил мой код. Этот шаблон действительно полезен только для утверждения возвращаемого значения. Если все, что вам нужно, это убедиться, что что-то вызывается, тогда вам придется использовать какую-то инъекцию для подачи вспомогательного класса, который будет выполнять DefinitionVehicleType, который вы можете заглушить с помощью Moq. Или, если вы открыты для этого, вы можете превратить свой DefinineVehicleType в общедоступный виртуальный метод и смоделировать его без ручного наследования. В этот момент вы можете настроить макет, чтобы убедиться, что он вызывается. - person Justin Pihony; 22.03.2012

Если говорить очень буквально, я полагаю, что ваш код демонстрирует запах.

Учтите следующее: метод ProcessVehicles вызывает метод экземпляра с именем DetermineVehicleType. Чем занимается ваш класс? Это Process Vehicles или Determine Vehicle Type? Это для меня указывает на нарушение SRP, если понимать это буквально. Ваш класс пытается выполнить более одной работы.

Возможно, это означает, что SRP не одобряет частные вспомогательные методы. Некоторые комментаторы действительно считают, что это так. Я не уверен, что знаю; как всегда главное - здравый смысл.

Если бы мне пришлось рефакторить этот код, я бы дал классу что-то вроде IVehicleCategoryHelper, которое выставляет DetermineVehicleType. Возможно, это будет передано через его конструктор или, если мы реализуем полнофункциональную инъекцию зависимостей Fat, IFactory, чтобы экземпляр мог получить IVehicleCategoryHelper, когда он ему нужен, в зависимости от контекста.

Примите все, что я сказал, с щепоткой соли. Я не обязательно считаю, что это правильный подход - в конечном итоге решать вам.

person Tom W    schedule 21.03.2012