Тестирование конкретного стороннего класса с JMock - PullRequest
3 голосов
/ 12 апреля 2009

У меня есть класс с методом переадресации 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?

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

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

Ответы [ 4 ]

5 голосов
/ 12 апреля 2009

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

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

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

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

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

4 голосов
/ 23 мая 2009

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

0 голосов
/ 31 декабря 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(); все это делается автоматически, прозрачным способом.

0 голосов
/ 21 мая 2009

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

...