Как использовать объекты EasyMock в методе JUnit @Before, а также в методе тестирования - PullRequest
0 голосов
/ 27 марта 2020

Я пытаюсь использовать EasyMock вместе с JUnit, и у меня возникли трудности при планировании вызовов метода для ложной зависимости в методе JUnit 4 @Before.

В следующем примере тестовый класс MockWithBeforeTest имеет вид тестирование класса ClassUnderTest. Dependency передается конструктору ClassUnderTest, в котором вызывается один из Dependency методов, возвращая значение, необходимое для инициализации ClassUnderTest. Этот процесс инициализации ClassUnderTest будет одинаковым для всех тестов, поэтому я украшаю метод ClassUnderTest#setUp аннотацией JUnit 4 @Before.

При тестировании метода ClassUnderTest#getDerived мы ожидаем вызова смоделированный экземпляр Dependency для возврата значения, которое мы планируем в методе MockWithBeforeTest#testGetDerived. Однако этот тест неожиданно завершается с ошибкой Unexpected method call Dependency.getB(), несмотря на то, что этот вызов запланирован в MockWithBeforeTest#testGetDerived.

Как мне изменить пример кода таким образом, чтобы MockWithBeforeTest#testGetDerived проходил?

Пример кода

import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMockRule;
import org.easymock.Mock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

public class MockWithBeforeTest {

  @Rule
  public EasyMockRule rule = new EasyMockRule(this);

  @Mock
  private Dependency dependency;

  private ClassUnderTest classUnderTest;

  @Before
  public void setUp() {
    expect(this.dependency.getA()).andReturn(2);
    replay(this.dependency);

    this.classUnderTest = new ClassUnderTest(this.dependency);
    verify(this.dependency);

  }

  @Test
  public void testGetDerived() {
    expect(this.dependency.getB()).andReturn(3);
    replay(this.dependency);

    assertEquals(6, this.classUnderTest.getDerived(1));
    verify(this.dependency);
  }

}

class ClassUnderTest {
  private int a;
  private Dependency dependency;

  ClassUnderTest(Dependency dependency) {
    this.a = dependency.getA();
    this.dependency = dependency;
  }

  void setA(int val) {
    this.a = val;
  }

  int getDerived(int val) {
    return val * this.a * this.dependency.getB();
  }

}

class Dependency {
  private int a;
  private int b;

  Dependency(int a, int b) {
    this.a = a;
    this.b = b;
  }

  int getA() {
    return this.a;
  }

  int getB() {
    return this.b;
  }

}

Stack Trace

java.lang.AssertionError: 
  Unexpected method call Dependency.getB():
    at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
    at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:101)
    at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97)
    at Dependency$$EnhancerByCGLIB$$6d3a4341.getB(<generated>)
    at MockWithBeforeTest.testGetDerived(MockWithBeforeTest.java:33)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.easymock.internal.EasyMockStatement.evaluate(EasyMockStatement.java:43)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)

Почему бы не создать экземпляр Dependency для передачи в конструктор?

Пример, приведенный выше, является представительным общей проблемы, в которой зависимость, передаваемая тестируемому классу, гораздо сложнее, чем Dependency.

Предположение о проблеме проектирования

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

Информация о версии программного обеспечения

  • Java: 1.8.0_201
  • JUnit: 4.12
  • EasyMock: 4,2

Ответы [ 2 ]

0 голосов
/ 30 марта 2020

Больше исследований и дискуссий с коллегами привело к решению. Я пропустил шаг, чтобы сбросить макет Dependency объекта , используя EasyMock.reset(this.dependency), чтобы добавить дополнительные ожидаемые вызовы в методы тестирования. Фиксированный MockWithBeforeTest - это

public class MockWithBeforeTest {

  @Rule
  public EasyMockRule rule = new EasyMockRule(this);

  @Mock
  private Dependency dependency;

  private ClassUnderTest classUnderTest;

  @Before
  public void setUp() {
    expect(this.dependency.getA()).andReturn(2);
    replay(this.dependency);

    this.classUnderTest = new ClassUnderTest(this.dependency);
    verify(this.dependency);
    reset(this.dependency); // Allow additional expected method calls to be specified
                            // in the test methods

  }

  @Test
  public void testGetDerived() {
    expect(this.dependency.getB()).andReturn(3);
    replay(this.dependency);

    assertEquals(6, this.classUnderTest.getDerived(1));
    verify(this.dependency);
  }

}
0 голосов
/ 30 марта 2020

replay() должен вызываться только один раз, после того, как все записано. Вот почему он здесь не работает.

Поскольку в вашем случае вы используете макет в конструкторе, вам нужно создать экземпляр протестированного класса после replay.

public class MockWithBeforeTest {

  @Rule
  public EasyMockRule rule = new EasyMockRule(this);

  @Mock
  private Dependency dependency;

  private ClassUnderTest classUnderTest;

  @Before
  public void setUp() {
    expect(this.dependency.getA()).andReturn(2);
  }

  @Test
  public void testGetDerived() {
    expect(this.dependency.getB()).andReturn(3);
    replay(this.dependency);

    this.classUnderTest = new ClassUnderTest(this.dependency);

    assertEquals(6, this.classUnderTest.getDerived(1));

    verify(this.dependency);
  }

}

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

public class MockWithBeforeTest {

  @Rule
  public EasyMockRule rule = new EasyMockRule(this);

  @Mock
  private Dependency dependency;

  private ClassUnderTest classUnderTest;

  @Before
  public void setUp() {
    expect(this.dependency.getA()).andReturn(2);
    replay(this.dependency);
    this.classUnderTest = new ClassUnderTest(this.dependency);
    reset(this.dependency);
  }

  @Test
  public void testGetDerived() {
    expect(this.dependency.getB()).andReturn(3);
    replay(this.dependency);

    assertEquals(6, this.classUnderTest.getDerived(1));

    verify(this.dependency);
  }

}
...