Как написать JUnit для адаптера без слишком сложного кода? - PullRequest
5 голосов
/ 28 июля 2011

У меня есть адаптер от I1 до ILogger, реализованный так:

class BAdapter() implements I1
{
   void logA() { // nothing }
   void logB() { new BLogger().log() }
   void logC() { // nothing }
}

Я хотел бы написать тест JUnit, который проверяет функциональность, но я нахожу это немного проблематичным, поскольку я не могу ввести свой объект Mock вместо BLogger или проверить возвращаемое значение. Я нашел несколько возможных решений, но я не уверен, что является лучшим.

Случай один: Добавьте void setLogger(Logger l) к классу BAdapter.

class BAdapter() implements I1
{
   private Logger logger = new BLogger();

   public void logB() { logger.log() }
   public void setLogger(Logger l) { logger = l }
   .. //rest of methods
}

Минусы: зачем добавлять сеттер, который никогда не используется в «реальном», не тестируемом коде?

Случай второй: Добавьте защищенный заводской метод и подкласс BAdapter в тестовом пакете.

class BAdapter() implements I1
{
   public void logB() { createLogger().log() }
   protected Logger createLogger() { retrun new BLogger() }
   .. //rest of methods
}

class BAdapterForTesting extends BAdapter()
{
   protected Logger createLogger() { retrun new MockBLogger() }
}

Минусы: я не уверен, если это чистое и элегантное решение, но я не вижу здесь много минусов.

Случай третий: Используйте шаблон Abstract Factory.

class BAdapter() implements I1
{
   public void logB() { AbstractFactory.getFactory().getBLogger().log() }
   .. //rest of methods
}

И где-то в тестах:

AbstractFactory.setFactory(new MockLoggersFactory())

Минусы: это слишком сложно, не так ли?

Дело четвертое: Возвратите логическое значение, например, при ведении журнала. Э.Г.

class BAdapter() implements I1
{
   Boolean logA() { return false; }
   Boolean logB() { return new BLogger().log() }
   Boolean logC() { return false; }
}

Минусы: это своего рода обходной путь. Зачем возвращать какое-то значение, когда оно никому не нужно в «реальном», не тестируемом коде?

Лучшее решение? Есть что-нибудь лучше?

Ответы [ 2 ]

2 голосов
/ 28 июля 2011

Из вашего кода трудно точно сказать, чего пытается достичь тестируемый класс, но Я бы остановился на первом случае с оговоркой, что я бы использовал инъекцию и в «реальном» коде.

Одним из преимуществ инъекций является то, что он делает класс, в данном случае ваш адаптер, более пригодным для повторного использования. Принудительный метод logB всегда делегировать экземпляру BLogger устанавливает это поведение в камне во время компиляции. Если я хочу, чтобы адаптер делегировал другому типу Logger, я не могу его использовать, и поэтому его можно использовать несколько реже.

0 голосов
/ 28 июля 2011

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

Это оставляет нам случай 3, который в целом является очень хорошим подходом, но если его использовать только во время тестирования, это все еще немного излишне.

Я бы предложил использовать некоторую дополнительную инфраструктуру для насмешек, такую ​​как PowerMockкоторые могут изменять закрытые поля в классах удобным способом:

class BAdapter() implements I1
{
   private Logger logger = new BLogger();

   public void logB() { logger.log() }
   .. //rest of methods
}

, а затем во время тестирования поменять поле на какое-то фиктивное:

Whitebox.setInternalState(loggerInstance, "logger", new MockLogger());

подробнее: http://code.google.com/p/powermock/wiki/BypassEncapsulation

Фреймворки Mocking являются большим преимуществом при тестировании, поэтому знание их очень помогает.

...