ПРИМЕЧАНИЕ. Полностью рабочий фрагмент кода следует в конце вопроса
У меня есть тест JMockIt для Service
класса с двумя зависимостями (Factory
и Consumer
во фрагменте). Служба внутренне запрашивает зависимость Factory
от объекта класса X
, а затем доставляет этот объект зависимости от Consumer
в этом методе:
public void interact(int i, E e) { X v = f.create(i, e); c.consume(v); }
В своем тесте я хочу использовать метод Service.interact()
и постулировать с помощью строгого ожидания, что потребитель получит значение параметра, которое удовлетворяет определенным свойствам. Теперь, поскольку параметр типа X
является POJO, который не реализует equals(), я хочу использовать его предоставленные методы getter для выполнения моих проверок; X
выглядит так (полный отрывок следует в конце вопроса):
public static class X { private final int i; public final E e;//... see below for full snippet public int getI() { return i; } public E getE() { return e; }//E is an enum
Так что я пытаюсь проверить, что потребитель получает значение с помощью X.getI() == 2
и X.getE() == EB
и я делаю это, используя как with(Delegate<X>)
и withArgThat(Matcher<X>)
в моем ожидании:
new StrictExpectations() {{ consumer.consume(withArgThat(new BaseMatcher() {//...//and new StrictExpectations() {{ consumer.consume(with(new Delegate() {//...
Однако обе проверки не срабатывают, потому что, когда они вызывают X.getE()
они получают то, что НЕ является значением перечисления EB
. Выходной сигнал модульного теста:
----------------------------------------- RUNNING CHECKS ON: i=2, e=B Direct reference match. Reference through getter mismatch! Ordinal through getter reference: 0 Identity hashcode from getter reference: 1282788025 Identity hashcode of expected reference: 519569038 ----------------------------------------- RUNNING CHECKS ON: i=2, e=B Direct reference match. Reference through getter mismatch! Ordinal through getter reference: 0 Identity hashcode from getter reference: 1911728085 Identity hashcode of expected reference: 519569038
Первый вопрос (название этого сообщения): почему JMockIt вмешивается в этот геттер в первую очередь? Насколько я могу судить, я не просил его издеваться над классом X, поэтому призывы к его методам getter должны входить в обычный X-образный X-код, но, по-видимому, это не так; порядковый номер возвращаемого значения перечисления равен нулю, что кажется значением по умолчанию для макета. Является ли это ожидаемым поведением, потому что я пропустил/неправильно понял что-то, или это может быть ошибка?
Второй вопрос: если у вас есть веская причина для поведения JMockIt, как я могу добиться того, чего хочу, и выполнить такой контроль над значениями, которые я могу получить только с помощью методов X getter? Как я могу исправить этот код, чтобы сообщить JMockIt просто оставить класс X
один.
Вот полный фрагмент, который будет компилироваться и запускаться (также на http://pastebin.com/YRL7Pdzv), у меня есть junit-4.12, hamcrest-core-1.3 и jmockit-1.14 в моем пути к классам и с использованием Oracle JDK 1.7.0_51,
package demonstrate; import mockit.Delegate; import mockit.Mocked; import mockit.NonStrictExpectations; import mockit.StrictExpectations; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Before; import org.junit.Test; public class SomeTest { @Mocked Factory factory; @Mocked Consumer consumer; @Before public void setUp() { new NonStrictExpectations() {{ factory.create(anyInt, (E) any); result = new Delegate() { @SuppressWarnings("unused") public X create(int i, E e) { return new X(i, e); } }; }}; } @Test public void testWithMatcher() { System.err.println("-----------------------------------------"); new StrictExpectations() {{ consumer.consume(withArgThat(new BaseMatcher() { final int i = 2; final E e = E.B; @Override public boolean matches(Object item) { return runChecks((X) item, i, e); } @Override public void describeTo(Description description) { description.appendText("\"i=" + i + ", e=" + e.name() + "\""); } })); }}; new Service(factory, consumer).interact(2, E.B); } @Test public void testWithDelegate() { System.err.println("-----------------------------------------"); new StrictExpectations() {{ consumer.consume(with(new Delegate() { @SuppressWarnings("unused") public boolean check(X x) { return runChecks(x, 2, E.B); } })); }}; new Service(factory, consumer).interact(2, E.B); } private static boolean runChecks(X actual, int i, E e) { System.err.println("RUNNING CHECKS ON: " + actual); if (actual.getI() != i) { System.err.println("Primitive int mismatch!"); return false; } if (actual.e == e) { System.err.println("Direct reference match."); } else { System.err.println("Reference mismatch!"); return false; } E otherE = actual.getE(); if (otherE != e) { System.err.println("Reference through getter mismatch!"); System.err.println("Ordinal through getter reference: " + otherE.ordinal()); System.err.println("Identity hashcode from getter reference: " + System.identityHashCode(otherE)); System.err.println("Identity hashcode of expected reference: " + System.identityHashCode(e)); return false; } return true; } public enum E { A, B } public static class X { private final int i; public final E e; public X(int i, E e) { this.i = i; this.e = e; } @Override public String toString() { return "i=" + i + ", e=" + e.name(); } public int getI() { return i; } public E getE() { return e; } } public static class Factory { public X create(int i, E e) { return new X(i, e); } } public static class Consumer { public void consume(X arg) { } } public static class Service { private final Factory f; private final Consumer c; public Service(Factory f, Consumer c) { super(); this.f = f; this.c = c; } public void interact(int i, E e) { X v = f.create(i, e); c.consume(v); } } }
Мне сказали в списке jmockit-users, что это на самом деле ошибка.
Рекомендуемым @Mocked(cascading = false) Factory factory;
было использование @Mocked(cascading = false) Factory factory;
и действительно, это заставляет вещи работать.