Унифицированные классы тестирования, которые имеют функциональность

23

При модульном тестировании выполняются функции класса, которые имеют частные функции, требующие оперативной функциональности. Как можно было бы проверить это?

Например:

public class Foo
{
    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        CloudService c = new CloudService();
        int oval = c.getValueFromService();
        return oval;
    }
}

Если бы я тестировал функцию: 'methodA ()', он попытался бы использовать 'goOnlineToGetVal ()', который, в свою очередь, попытался бы выйти в Интернет, однако, если бы этот тест был выполнен без функциональности. Как бы я прошел 100% покрытие класса, не выходя в интернет?

Бен сошел с ума Euden
источник
Ваш метод просто вызывает облачный API или выполняет дополнительную работу?
Уинстон Эверт
4
Внедрение зависимости ?
PhaDaPhunk

Ответы:

76

new CloudService()

И есть твоя проблема.

Современный дизайн ОО рекомендует передавать такого рода зависимости, а не создавать их напрямую. Это может быть передано в саму функцию или в класс во время создания. Он также может быть захвачен или агрегирован контейнером Inversion of Control, если такая сложность оправдана.

В этот момент становится довольно тривиальным использовать поддельный / поддельный сервис для предоставления вам ваших «онлайн» данных во время тестирования. Более того, он позволяет вашему программному обеспечению быть достаточно гибким, чтобы вы могли быстро адаптироваться, если какой-нибудь (государственный?) Клиент придет и не захочет использовать облако для своих ценностей. Или вы хотите сбросить один облачный провайдер за другим. Или...

Telastyn
источник
7
Я думаю, я просто стал намного ближе к пониманию того, что такое чертова инъекция зависимостей.
marczellm
Где лучше всего создать все экземпляры, которые нужны моему приложению? Как можно ближе к вершине моего приложения? Пока что вы можете применять только внедрение зависимостей; в какой-то момент вам нужно создавать экземпляры самостоятельно, а не передавать их в качестве аргументов.
Пол
3
@ Пол - Это зависит. По моему опыту, приложения очень часто выглядят как двоичные деревья - один класс / функция / модуль склеивает два вместе, чтобы обеспечить более сложное поведение. Один из этих классов «склеивания» - это тот, который определяет конкретную реализацию, которая, в свою очередь, используется чем-то выше, что склеивает его с чем-то другим, что, в свою очередь ... до тех пор, пока вы в конечном итоге не доберетесь до своей точки входа, которая склеивает большие части когерентно. «Лучшее» место - это место, которое позволяет вашему коду делать то, что ему нужно, не заставляя его делать или знать о том, чего не следует делать. Это будет меняться.
Теластин
2
@Paul Обычно ваше приложение разделено между «библиотекой», которая тестируется напрямую и должна использовать этот вид внедрения зависимостей, и кодом, специфичным для приложения. Последний обычно сводится к некоторому графическому интерфейсу, веб-службе или интерфейсу командной строки, который напрямую вызывает библиотеку с данными, предоставленными пользователем. По сути, специфичный для приложения код создаст конкретные реализации, которых ожидает библиотека с зависимостями.
Darkhogg
@Paul Посмотрите на контейнеры IoC, такие как Castle Windsor, StructureMap, Ninject и Unity. Они ни в коем случае не являются единственным способом внедрения зависимости, но они могут быть очень информативными с точки зрения размышлений о построении графа объектов сверху вниз
Бен Ааронсон
37

Я бы реализовал это так:

public class Foo
{
    private ICloudService cloudService;

    public Foo(ICloudService s)
    {
       cloudService=s;
    }

    public int methodA()
    {
         int val = goOnlineToGetVal();

         return val;
    }

    private int goOnlineToGetVal()
    {
        int oval = cloudService.getValueFromService();
        return oval;
    }
}

Интерфейс ICloudServiceможет быть реализован либо с макетом для тестирования, либо с «настоящим» облачным сервисом. Когда конкретизации new CloudService()являются обязательной для каждого вызова getValueFromService, или CloudServiceот 3 - й партии API , который не может быть изменен, реализовать обертку, вытекающая из ICloudServiceи делают соответствующие вызовы.

Док Браун
источник
1
Это определенно путь. Существуют библиотеки классов, которые могут макетировать интерфейс, предоставляя вам полный контроль над тестом.
Грег Бургхардт