Тестирование конкретного стороннего класса с помощью JMock

У меня есть класс с методом пересылки foo:

void foo( Concrete c, String s ) { c.bar( s ); }

Я хочу проверить, действительно ли foo продвигается вперед. К сожалению, для меня Concrete - это класс в сторонней библиотеке и конкретный тип, а не интерфейс. Таким образом, я должен использовать ClassImposteriser в JMock для имитации Concrete, поэтому в моем тестовом примере я делаю следующее:

@Test
public final void testFoo() {
   Mockery context = new JUnit4Mockery() {{
      setImposteriser(ClassImposteriser.INSTANCE);
   }};

  final Concrete c = context.mock(Concrete.class);
  final String s = "xxx" ;

  // expectations
  context.checking(new Expectations() {{

     oneOf (c).bar(s); // exception gets thrown from here
  }});


  new ClassUnderTest.foo( c, s );
  context.assertIsSatisfied();

}

К сожалению, Concrete.bar, в свою очередь, вызывает метод, который выбрасывает. Этот метод окончательный, поэтому я не могу его отменить. Кроме того, даже если я закомментирую строку new ClassUnderTest.foo( c, s );, исключение выдается, когда JMock устанавливает исключения, а не при вызове foo.

Итак, как я могу проверить, что метод ClassUnderTest.foo пересылает Concrete.bar?

Изменить:
Да, бар окончательный.

Мое решение, которое не является общим, заключалось в использовании класса "Tester" в сторонней библиотеке для правильной настройки Concrete.


person tpdi    schedule 12.04.2009    source источник


Ответы (4)


Из текста вопроса не ясно, является ли Concrete.bar () окончательным или Concrete.somethingElse () является окончательным и вызывается из Concrete.bar ().

Если Concrete.bar () не является final, создайте рукописную заглушку для Concrete следующим образом:

public class ConcreteStub extends Concrete
{
    public int numCallsToBar = 0;
    @Override
    public void bar(String s) { numCallsToBar++; }
}

и в вашем тестовом коде:

ConcreteStub c = new ConcreteStub();
foo(c,"abc");
assertEquals(1,c.numCallsToBar);

Если Concrete.bar () является окончательным, это более сложно, и ответ зависит от сложности Concrete и того, как ваш проект использует класс Concrete. Если ваше использование Concrete достаточно просто, я бы подумал об использовании Concrete в интерфейсе (Шаблон адаптера ), которую потом можно будет проще высмеивать.

Преимущества решения «Шаблон адаптера»: возможно прояснить поведение, назвав интерфейс после того, как в вашем проекте используется Concrete. Легче проверить.

Недостатки решения «Шаблон адаптера»: вводит больше классов, возможно, с небольшой пользой для производственного кода. Я не знаю, что делает Concrete, и, возможно, нецелесообразно использовать Concrete в интерфейсе.

person Alex B    schedule 12.04.2009

См. http://www.jmock.org/mocking-classes.html для получения информации. о моковых классах и о том, как обойти финальные ограничения.

person Nat    schedule 22.05.2009
comment
Не могли бы вы немного расширить этот ответ, содержащий только ссылку? Я не отмечал, потому что вижу, что вы все еще активный пользователь, и это старый ответ, но было бы полезно еще одно объяснение. - person Pops; 06.09.2012

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

person Steve Freeman    schedule 21.05.2009

Используйте более эффективный инструмент имитации, например JMockit. Тогда ваш тест может быть записан как:

@Test
public void testFoo(final Concrete c)
{
  final String s = "xxx";

  new Expectations() {{
    c.bar(s);
  }};

  new ClassUnderTest().foo(c, s);
}

Для JMockit не имеет значения, является ли Concrete интерфейсом, конечным классом, абстрактным классом или чем-то еще. Кроме того, нет необходимости использовать @RunWith, расширять базовый тестовый класс или вызывать какой-либо метод, например assertIsSatisfied(); все это делается автоматически и прозрачно.

person Rogério    schedule 31.12.2009