2010-01-11 11 views
7

Я начал использовать Guice, чтобы сделать некоторую инъекцию зависимостей в проекте, прежде всего потому, что мне нужно вводить издевательства (используя в настоящее время JMock) слой из теста unit, что делает ручную инъекцию очень неудобной.Каков наилучший способ использования Guice и JMock?

Мой вопрос - лучший способ введения макета? То, что я в настоящее время, чтобы сделать новый модуль в модульном тесте, который удовлетворяет зависимости и связать их с провайдером, который выглядит следующим образом:

public class JMockProvider<T> implements Provider<T> { 
    private T mock; 

    public JMockProvider(T mock) { 
     this.mock = mock; 
    } 

    public T get() { 
     return mock; 
    } 
} 

Передача издеваются в конструкторе, поэтому установка JMock может выглядеть это:

final CommunicationQueue queue = context.mock(CommunicationQueue.class); 
    final TransactionRollBack trans = context.mock(TransactionRollBack.class); 
    Injector injector = Guice.createInjector(new AbstractModule() { 
     @Override 
     protected void configure() { 
      bind(CommunicationQueue.class).toProvider(new JMockProvider<QuickBooksCommunicationQueue>(queue)); 
      bind(TransactionRollBack.class).toProvider(new JMockProvider<TransactionRollBack>(trans)); 
     } 
    }); 
    context.checking(new Expectations() {{ 
     oneOf(queue).retrieve(with(any(int.class))); 
     will(returnValue(null)); 
     never(trans); 
    }}); 
    injector.getInstance(RunResponse.class).processResponseImpl(-1); 

Есть ли лучший способ? Я знаю, что AtUnit пытается решить эту проблему, хотя мне не хватает того, как она автоматически вводит макет, созданный локально, как указано выше, но я ищу либо убедительную причину, почему AtUnit - правильный ответ здесь (другой чем его способность изменять DI и насмешливые рамки без изменения тестов), или если есть лучшее решение для этого вручную.

ответ

12

Вам не нужно вводить издевательства, хотя каркас DI. Я использую Guice и JMock довольно успешно, и мои модульные тесты не ссылаются ни на что связанное с Guice. Я использую только mocks и передаю в null, где применимо.

DI разрешит впрыскивание и построение зависимостей текущего класса, поэтому, если вы хотите добавить класс издевательства (который эффективно останавливает график зависимостей), вам просто нужно передать его. Misko Hevery изложено в одном из Google Tech Talks, что модульные тесты должны быть усеяны new и null, потому что их область локализации зависит от метода единичного тестирования - я должен согласиться с ним.

Есть ли причина, по которой необходимо использовать Guice в тестах, то есть, если они не являются функциональными/интеграционными тестами?

Не будет ли тестовая работа, если она исключит форсунку? Не могли бы вы реорганизовать тест на что-то вроде:

final CommunicationQueue queue = context.mock(CommunicationQueue.class); 
final TransactionRollBack trans = context.mock(TransactionRollBack.class); 

context.checking(new Expectations() {{ 
    oneOf(queue).retrieve(with(any(int.class))); 
    will(returnValue(null)); 
    never(trans); 
}}); 

RunResponse r = new RunResponse(queue, trans); // depending on the order 
r.processResponseImpl(-1); 
+0

В конкретном примере вы совершенно правы, что DI не нужен для издевательств (если я сделал закрытый пакет конструктора, я думаю), но отсюда у меня есть зависимости, которые нужно ввести в два шага от тестового кода (RunResponse получает имя класса из очереди и создает его экземпляр), поэтому я смотрел на Guice, чтобы сделать полевую инъекцию для этой цели. – Yishai

+4

Я вижу. Ничто не мешает вам использовать Guice в тестах, но, если это возможно, я предлагаю попробовать и избежать этого (если вы не тестируете уровень функциональности/интеграции) Проблема в том, что вы нарушаете Закон Деметры , Ваш текущий класс должен только беспокоиться о его непосредственных зависимостях, и переходные зависимости не должны приниматься во внимание. В модульных тестах для текущих зависимостей классов вы делаете то же самое и учитываете только непосредственные зависимости.Такая схема делает ваш код более чистым и более дружественным к DI :) – gpampara

1

Из того, что вы сказали, что это звучит, как вы не делаете реальную юнит-тест. При выполнении unit-теста для класса вы фокусируетесь только на одном классе. Когда вы выполняете свой блок-тест, выполнение должно выполнять только испытуемый класс и избегать запуска методов из связанных классов.

JMock/Easymock - это инструменты, которые помогут достичь модульного тестирования вашего класса. Они не полезны в тесте интеграции.

В контексте модульного тестирования Guice перекрывается с JMock/Easymock. Guice заставляет пользователей загрязнять свой код всеми видами аннотаций (например, @Inject) для достижения того же результата, что и JMock/Easymock. Я действительно сильно не согласен с Гисом в том, как они помогают в тестировании. Если разработчикам приходится использовать Guice вместо JMock/Easymock, они должным образом либо не выполняют реального модульного теста, либо пытаются тестировать классы контроллеров.

Класс контроллера тестирования не должен выполняться на уровне единицы измерения, но на уровне интеграции. Для теста интеграции использование Jakarta Cactus очень полезно. В тесте интеграции разработчики должны иметь все зависимости и, следовательно, нет необходимости использовать Guice.

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

+0

Спасибо Thang, это в основном, где я сейчас, в большинстве случаев я не использую Guice для тестирования. Я нашел Guice, чтобы управлять лучшим дизайном, поэтому он определенно заработает. Однако в моем случае принцип «единичного теста проверяет только один класс» невозможен, поскольку один класс должен привести к созданию экземпляра другого через фреймворк. Это сложно, потому что это так, но нет хорошего пути (как и без других недостатков, которые влияют на продуктивность) вокруг него - я пробовал. – Yishai

+0

Jakarta Кактус вышел на пенсию: http://jakarta.apache.org/cactus –

0

Yishai, я должен исправить это , управляя лучшим дизайном. следует приписывать шаблону инжекций зависимостей, представленному Мартином Фаулером. Guice - это инструмент, который поможет вам, если ваши коды уже соблюдены. Guice ничего не способствует вашему лучшему кодированию. Guice решает DI в режиме ad-hoc (на уровне аннотации), поэтому его нельзя считать используемым как основа DI. Это большая разница между контейнером DI на основе xml весной и контейнером DI на основе аннотаций от Guice.

Я потратил некоторое время, чтобы прочитать ваш код, опубликованный в начале (я обычно этого не делаю), чтобы понять, почему вы сказали, что модульный тест вашего класса невозможен. Я, наконец, понимаю проблему, с которой вы столкнулись. Из того, что я вижу на уровне единичного теста, вы пытались имитировать поток выполнения части кода, когда он запускается внутри Java EE-сервера. Вот почему ты застрял. Я не знаю, каков стимул здесь, но я боюсь, что ваши усилия по подготовке всех этих экологических зависимостей будут потрачены впустую :(

Письменный блок test предназначен для проверки «бизнес-логики», которая не зависит от независимо от технологии вы используете. Там может быть в стороне требование о том, что это бизнес-логика должна быть выполнена в качества обслуживания (QoS) среды. Это требование «технические требования». В вашем примере, CommunicationQueue и TransactionRollBack в основном работают для удовлетворения «технических требований». Ситуация заключается в том, что ваши «бизнес-требования» окружены всеми этими экологическими единицами. Что вам нужно сделать, это рефакторинг вашего c ode, так что ваше «деловое требование» находится в разделенных методах, которые не имеют зависимостей. Затем вы пишете unit-test для этих методов.

Я все еще считаю, что вы выполняете функциональное тестирование. Выполнение функциональных тестов на уровне единичного теста очень трудоемко, и делать это не стоит. Я надеюсь, что это поможет

+0

Что касается Guice, я думаю, что подход аннотации лучше по соображениям улучшения статической проверки и уменьшения дублирования, но я согласен с тем, что Guice - это инструмент для концепция дизайна. Что касается того, зачем мне это нужно, слишком долго вписываться в комментарий, но очень короткий код требует, чтобы контейнер был посередине, поэтому я пытаюсь выполнить модульный тест кода без контейнера (вызов по одному чтобы получить результат на другом), но они имеют разные зависимости, должны быть в разных классах, и каждый из них должен быть создан через код рамки. – Yishai

0

Yishai, кажется, что вам нужен AOP, чтобы обойти эти зависимости. AspectJ и AspectWerkz - оба полезные ресурсы. Тем не менее, AspectWekz имеет очень приятную особенность плетения классов аспект во время исполнения с использованием технологии JVM hotswap. Конечно, эти инженерные усилия должны применяться только на уровне единичного тестирования.

Я приложил много усилий для решения зависимостей при проведении модульного тестирования. Такие инструменты, как JMock, EasyMock, Java EE micro-container, AspectJ, AspectWerkz, Spring IoC могут использоваться для решения проблемы DI. Ни один из этих инструментов не заставляет разработчиков размещать тестирование в своих производственных кодексах. Слишком плохо, что Guice нарушает концепцию сохранения ваших кодов от любых видов тестирования. И я не считаю это инструментом, который будет использоваться в моем модульном тесте.

Надеюсь, я дал вам достаточно оснований для того, чтобы принять @Inject аннотацию с вашего производственного кода.

+0

Thang, я не знаю, почему вы думаете, что Guice делает ваш код более «проверенным», чем Spring, AspectJ или AspectWekz, но вы не дали мне никаких оснований, просто альтернатив и утверждений. – Yishai

+0

AspectJ & AspectWekz принимает вопрос DI на уровне байт-кода. AspectJ использует компилятор 'ajc', чтобы вводить аспектное поведение в классы байт-кода. AspectWekz идет дальше без использования ajc, используя технологию hotswap bytecode. Эти два инструмента работают в режиме предварительной компиляции, времени компиляции или времени загрузки класса для выполнения инъекции. Весна работает немного на более высоком уровне, используя DI на основе xml. Конечно, ваш проект уже должен принять Spring, чтобы воспользоваться этим преимуществом (например, получить экземпляр через заводы) – Thang

Смежные вопросы