Фон:
Это конкретный вопрос JMock + JUnit (это две технологии я должен). Да, то, что я хочу сделать, может быть сделано с помощью PowerMock, но это крайний случай, который не требует изменения инструментов. И нет, извините, я не задаю этот вопрос для обсуждения философской действительности статических методов:)
С этой точки зрения я действительно благодарю любого, кто смотрит на этот вопрос.
Вопрос:
У меня есть часть устаревшего кода, который мне нужен для написания теста (мы пытаемся поставить тесты вокруг унаследованного кода, чтобы гарантировать, что мы не сломаем ничего во время потенциально массивного процесса рефакторинга... что сказка в другое время.)
Цель:
Метод, который я пытаюсь высмеять, - это метод Foo.bar
в следующем классе, используя средство импостерирования класса JMock (через JUnit4Mockery.)
Код ниже является репрезентативным для кода, который я тестирую:
public class Foo {
public abstract <T> void bar(
Class<? extends T> paramClass, T paramT);
Моя тестовая установка нацелена на то, чтобы любое количество вызовов bar()
получало экземпляр класса (который, очевидно, вырождается в класс... глупое стирание стирания стилей Java), в сочетании с любым экземпляром Snafu.
Это ключевое различие здесь. Я не соединяю два параметра Matcher или два литерала, но один литерал (T.class) и любое значение типа T. JMock не позволяет это, поэтому ожидаемым решением было бы иметь Matcher > и Matcher:
Foo mock = context.mock(Foo.class);
context.checking(new Expectations() {
// keep warnings close to the culprit code when possible
@SuppressWarnings("unchecked")
public void allow(final Foo mockedFoo) {
allowing(mockedFoo).bar(
with(any(Snafu.class.getClass())), // Matcher that *should* resolve to Class<?>
with(any(Snafu.class))); // matcher to anything of type Snafu.class
}
{
allow(mockedFoo);
}
});
Затем мы вводим издеваемое Foo, которое в итоге получится так называемым другим классом, которое я назову как Driver
(* Я вернусь к вызову статического метода позже):
// fooImpl has been replaced/injected with our mock
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
Проблема:
Проблема заключается в том, что когда Driver
вызывает метод bar
на посмеянный экземпляр Foo
, мой тест встречается со следующим исключением:
java.lang.IllegalArgumentException: not all parameters were given explicit matchers: either all parameters must be specified by matchers or all must be specified by values, *you cannot mix matchers and values*
at org.jmock.internal.InvocationExpectationBuilder.checkParameterMatcherCount(InvocationExpectationBuilder.java:98)
at org.jmock.internal.InvocationExpectationBuilder.createExpectationFrom(InvocationExpectationBuilder.java:91)
at org.jmock.internal.InvocationToExpectationTranslator.invoke(InvocationToExpectationTranslator.java:19)
at org.jmock.internal.FakeObjectMethods.invoke(FakeObjectMethods.java:38)
at org.jmock.lib.legacy.ClassImposteriser$4.invoke(ClassImposteriser.java:129)
at .....
По-видимому (или так мне кажется), JMock matchers 'видят Class
экземпляры как значения, независимо от того, как мы пытаемся их сопоставить. Или я что-то упускаю?
Я встречаю подобные исключения во многих унаследованных вызовах, которые принимают аргумент java.lang.Class
. Очевидно, что все, что выглядит как X.class
, будет значением, а не новым экземпляром.
Но в этом проблема, потому что другой аргумент должен быть разрешен с помощью сочетания, а не только с фактическим значением.
[*] В идеале можно переписать вызов статического метода в
fooImpl.bar(Snafu.class, someStaticFunctionThatReturnsASnafu());
с чем-то более поддающимся насмешкам (нестатический метод, другой объект или что-то введенное с помощью IoC).
Возможно, именно так мы и закончим, но пока что данный код имеет значительное количество статических вызовов.
Я хотел бы отложить это до более подходящего момента и вместо этого найти общее решение JMock, если оно существует, что позволяет мне установить необходимые ожидания насмешливых функций, подобных Foo.bar
выше.