Как провести модульное тестирование класса, который расширяет/наследует сторонний класс

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

Представленный ниже AbstractDecoratorMapper — это специальный класс SiteMesh, который я должен расширить, чтобы SiteMesh работал правильно. Насколько я могу судить из документации, я не могу использовать композицию.

public final class PartnerDecoratorMapper extends AbstractDecoratorMapper {

    @Override
    public void init(Config config, Properties properties, DecoratorMapper parent) throws InstantiationException {
        super.init(config, properties, parent);
    }

    @Override
    public Decorator getDecorator(HttpServletRequest request, Page page) {
        if (super.getDecorator(request, page).getName().equalsIgnoreCase("default")) {
            return getNamedDecorator(request, "externalPartnerDefault");
        }
        return super.getDecorator(request, page);
    }
}

Я использую JMock, если этот инструмент может чем-то помочь.


person Ilyas Patel    schedule 11.06.2012    source источник


Ответы (4)


Есть две вещи, которые вы можете спросить. Я постараюсь ответить обоим.

Возможный вопрос 1) Как протестировать Дополнительный функционал?

Возможный вопрос 2) Как проверить, подходит ли объединенная функциональность?

Начну с Вопроса 1.

Я бы посоветовала простой пилинг. Здесь есть видео этой техники: http://www.youtube.com/watch?v=p0tILwRZH5Q (видео на С#, но в основном идентично на java)

что означало бы, что после этого у вас будет следующий код

@Override
public Decorator getDecorator(HttpServletRequest request, Page page) {
    Decorator d = super.getDecorator(request, page);
    return getResolvedDecorator(d, d.getName(), request);

}
public Decorator getResolvedDecorator(Decorator current, String name, HttpServletRequest request) {
    if (name.equalsIgnoreCase("default")) {
        return getNamedDecorator(request, "externalPartnerDefault");
    }
    return current;
}

Теперь вы можете проверить это с помощью вызова, например

assertEquals(expected, new PartnerDecoratorMapper().getResolvedDecorator(null, "default", MockHttpServletRequest);

Я бы предложил также удалить данные из HttpServletRequest, чтобы сделать тестирование и намерение более ясным.

Примечание: это имеет дополнительное преимущество в производительности, поскольку вызов super.getDecorator() происходит только один раз, поскольку результат кэшируется.

Дополнительное примечание. Также стоит отметить, что переопределение init() не требуется и фактически ничего не делает.

Вопрос 2: Это более сложный вопрос, поскольку вы не указываете желаемое поведение. Я предполагаю, что это заводской шаблон, поэтому, скорее всего, он будет работать как-то так

| качество HTTP-запроса 1 | Качество страницы 1| Ожидаемый декоратор | | /мой путь/моя страница | статус = фу | Мой Декоратор |

(Я понятия не имею, что должна содержать или выглядеть эта приведенная выше диаграмма). Если у вас есть такое поведение, тогда проверьте, чтобы убедиться, что оно будет довольно простым.

Удачных испытаний, Ллевеллин

person llewellyn falco    schedule 11.06.2012
comment
Спасибо, что потратили время на этот ответ. Я сделал небольшую ошибку, но в вашем новом методе (getResolvedDecorator) вызовом getNamedDecorator() будет super.getNamedDecorator(). Изменит ли это вашу реализацию, поскольку это вызывает сторонний расширенный класс? - person Ilyas Patel; 11.06.2012
comment
Не за что. ответ зависит от того, насколько сложно настроить и вызвать getNamedDecorator(). Если это сложно, я, как правило, хочу избежать этого, но в целом вы все равно хотите сосредоточиться на заданных характеристиках запроса страницы и URL-адреса a, b, c, ожидающих декоратора типа d. - person llewellyn falco; 12.06.2012
comment
и с небольшим рефакторингом ваши тесты должны выглядеть как эти две строки - person llewellyn falco; 12.06.2012

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

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

Чтобы проверить, был ли выполнен весь реализованный вами код, вы можете использовать плагин для проверки покрытия, такой как eclemma - http://www.eclemma.org/ )

person Francisco Spaeth    schedule 11.06.2012
comment
В моем тесте мне нужно было бы вызвать метод init() с тремя параметрами, и я действительно не знаю, что это такое, поскольку SiteMesh создает их для каждого запроса страницы. Итак, в моем тесте мне все равно нужно будет создавать эти параметры, пока тест не заработает, так что это просто вопрос проб и ошибок? - person Ilyas Patel; 11.06.2012
comment
Если вы просто хотите протестировать свой фрагмент кода, вы можете создать его экземпляр с несколькими приемлемыми параметрами и переопределить в своем тесте (реализация, спускающаяся по вашему классу, специфичная для вашего теста, в которой вы переопределите некоторые методы, которые должны возвращать ожидаемое значение) методы ваш код зависит от. - person Francisco Spaeth; 11.06.2012

Насколько мне известно, с JMock вы не можете имитировать вызовы суперкласса тестируемого класса.

Ответы на этот связанный вопрос могут быть полезны, но Основной ответ — переключиться на фиктивный фреймворк (предлагается JMockit), который это поддерживает.

person Don Roby    schedule 11.06.2012

так как я не знаю точных деталей, которые требует сторонний класс

Тестирование любого рода сравнивает то, что что-то на самом деле делает с тем, что оно должно делать. Вы не можете тестировать, пока не узнаете, что что-то должно делать: «что требует класс 3-й части».

И как вы можете предпринять какие-либо разумные попытки написать свой производный класс, если вы не знаете, что требуется базовому классу? Что говорится в документации API для класса, который вы расширяете?

Возможно, проблема в том, что такой документации по API нет или она малоинформативна? Возможно, хотя класс и не final, на самом деле он не предназначен для использования в качестве базового класса. Любой из них указывает на плохо спроектированный API и предлагает вам не использовать его.

Или критическая трудность заключается в том, что вы не знаете точных деталей? Это может быть недостатком вашего подхода: обычно вы не можете и не должны делать предположения о точных деталях. Ваш код должен соответствовать документированному API, и вы не должны принимать ничего, что не указано в документированном API. Это может означать, что вы не можете знать, когда могут быть вызваны ваши методы шаблона или какие значения будут переданы им. Если базовый класс взаимодействует с вашими методами шаблона, передавая объекты, объявленные для реализации класса interface или не-3_ (то есть их тип, но не их класс, указано), вы не можете знать, к какому классу будут относиться эти объекты.

person Raedwald    schedule 11.06.2012