Тестирование конкретного стороннего класса с 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?

Редактировать:
Да, бар является окончательным.

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

4 ответа

Решение

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

Если Concrete.bar() не является окончательным, создайте рукописную заглушку для 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. Если вы используете бетон достаточно просто, я бы подумал обернуть бетон в интерфейс ( шаблон адаптера), который затем можно будет легко смоделировать.

Преимущества решения Adapter Pattern: возможно, проясните поведение, назвав интерфейс после использования вашего проекта в Concrete. Проще проверить.

Недостатки решения Adapter Pattern: вводит больше классов с минимальными преимуществами для производственного кода. Я не знаю, что делает Бетон, и это может быть не практично, чтобы обернуть Бетон в интерфейсе.

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

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

Используйте более способный инструмент для насмешек, такой как 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(); все это делается автоматически, прозрачным способом.

Другие вопросы по тегам