Java: Как мне выполнить модульное тестирование, когда есть инкапсулированные внедренные или сгенерированные зависимости? - PullRequest
0 голосов
/ 28 декабря 2018

РЕЗЮМЕ: У меня есть автономный класс для выполнения прокси-входа в систему и последующего заполнения объекта HttpServletResponse содержимым аутентификации, которое может использовать браузер.Когда я тестирую свой код, как я могу предоставить имитируемые сервисы в классе, в котором нет сеттеров?

ПОДРОБНОСТИ: Я серьезно отредактировал свой прокси-код входа в систему в этом фрагменте.

  1. Запрашивает у сервера форму для входа.
  2. Отправляет учетные данные.
  3. Получает одобрение сервера и передает его браузеру (ответ).

Урезанный код выглядит следующим образом:

    private static final Log log = LogFactory.getLog(MyClass.class);

    @Inject()
    private UserService userService;

    public void performProxyLogin(HttpServletResponse response, 
            UserDTO userDTO, String url) {

        String username = getUsername(userDTO);
        String password = getPasswordFromUserService(username);

        // MyRequest only has data, organizing a Http Request.
        MyRequest myRequest = prepareInitialGetRequest(url);  

        // processURLRequest() encapsulates use of HttpURLConnection.
        // MyResponse only has data, organizing a Http Response.
        MyResponse myResponse = processURLRequest(myRequest); 
        myRequest = prepareLoginRequest(myResponse, username, password);
        myResponse = processURLRequest(myRequest);

        // Transfer data into the response, and from there into the browser.
        fillResponseWithProxiedResult(response, myResponse)
    }

Чтобы сделать эту работу, я думаю, мне нужно ввести поддельный журнал или LogFactory, поддельный UserService и способполучение поддельного HttpURLConnection.

Однако все советы, которые я видел, касаются кода с установщиками, который набор тестов может использовать для подключения поддельных объектов.

Как предоставить свой классего нужны издевательские объекты?

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Прикуси пулю и предоставь для этого поля личный установщик пакета.

Если вы хотите использовать mocks, то нет смысла позволять инфраструктуре впрыска создавать mock, который вы можете внедрить, так как вы 'добавление дополнительных церемоний и накладных расходов к настройке теста.

Если вы хотите проверить, правильно ли вы вводите службу, вы вообще не хотите использовать макеты (подумайте «интеграционный тест»).с реальными или псевдо-реальными компонентами).

0 голосов
/ 28 декабря 2018

Многие другие ответы намекают на это, но я собираюсь более четко сказать, что да, наивные реализации внедрения зависимостей могут нарушить инкапсуляцию.Ключом к тому, чтобы избежать этого, является то, что вызывающий код не должен напрямую создавать экземпляры зависимостей (если они не заботятся о них).Это можно сделать несколькими способами.Самый простой - просто иметь конструктор по умолчанию, который выполняет внедрение со значениями по умолчанию.Пока вызывающий код использует только конструктор по умолчанию, вы можете изменять зависимости за кулисами, не влияя на вызывающий код.Это может начать выходить из-под контроля, если ваши зависимости сами имеют зависимости и так далее.В этот момент может появиться шаблон Factory (или вы можете использовать его с самого начала, чтобы вызывающий код уже использовал Factory).Если вы представляете фабрику и не хотите ломать существующих пользователей вашего кода, вы всегда можете просто вызвать фабрику из конструктора по умолчанию.Помимо этого, используется Inversion of Control.Я недостаточно использовал IoC, чтобы говорить об этом слишком много, но здесь есть много вопросов, а также статей в Интернете, которые объясняют это гораздо лучше, чем я мог.Если он должен быть действительно инкапсулирован там, где вызывающий код не может знать о зависимостях, тогда есть возможность либо сделать внедрение (либо конструктор с параметрами зависимости, либо установщиками) внутренним, если язык поддерживает это, либо сделать их приватными и иметь вашимодульные тесты используют что-то вроде Reflection, если ваш язык поддерживает это.Если ваш язык не поддерживает ни то, ни другое, я полагаю, возможно, что класс, вызывающий код, создает экземпляр фиктивного класса, который просто инкапсулирует класс, который выполняет реальную работу (я считаю, что это шаблон Facade, но я никогда не помню правильно имена)]

...