JUnit测试方法调用之间丢失了模拟状态

时间:2016-08-16 19:53:36

标签: java testing junit mocking mockito

我不明白为什么test1()会失败,尽管它与test2()相同。另一种测试方法成功了......

我在assertTrue(str.equals("hello"));

中获得了NPE
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import junit.framework.TestCase;

class UnderTest {
    private static UnderTest instance;
    private ToMock toMock;

    public void doSomething() {
        String str = toMock.get();
        assertTrue(str.equals("hello"));
    }

    public static UnderTest getInstance() {
        if (instance == null) {
            instance = new UnderTest();
        }
        return instance;
    }

    public void set(ToMock toMock) {
        this.toMock = toMock;
    }

    public ToMock get() {
        return toMock;
    }
}

public class SomeTest extends TestCase {
    private ToMock toMock;
    private UnderTest underTest;
    private String str;

    public SomeTest() {
        toMock = mock(ToMock.class);

        doAnswer(new Answer<Void>() {
            public Void answer(InvocationOnMock invocation) {
                Object[] args = invocation.getArguments();
                str = (String) args[0];
                return null;
            }
        }).when(toMock).set((String) org.mockito.Mockito.any());

        when(toMock.get()).thenAnswer(new Answer<String>() {
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                return str;
            }
        });

        UnderTest.getInstance().set(toMock);
    }

    @Before
    public void setUp() throws Exception {
        //      UnderTest.getInstance().set(toMock);
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void test1() {
        toMock.set("hello");
        UnderTest.getInstance().doSomething();
    }

    @Test
    public void test2() {
        toMock.set("hello");
        UnderTest.getInstance().doSomething();
    }
}

下面的界面应该放在一个额外的文件中。否则它不能被 Mockito 嘲笑。

public interface ToMock {
    void set(String str);
    String get();
}

但是一旦我取消注释:

@Before
public void setUp() throws Exception {
    //      UnderTest.getInstance().set(toMock);
}

这两种方法都会成功。我看不出这条指令如何影响str字段。在strnull的调用之间看起来test1()设置为test2()。但为什么和在哪里?据我所知,我不必只是为了保持某些字段的当前值而调用setUp()。在JUnit中的测试方法调用之间不应该丢失SomeTest(包括str)的状态。

如何解释?

2 个答案:

答案 0 :(得分:4)

你正在混合JUnit 3和JUnit 4.不要。摆脱

extends TestCase {

这可能会让您的测试运行者使用JUnit 3来运行测试。

JUnit总是创建一个测试类的新实例来运行每个测试方法。

在JUnit 3中,运行器在执行任何方法之前准备所有实例。在你的情况下,执行基本上就像这样

  1. 实例化SomeTest,实例 A
  2. 在实例 A
  3. 上调用构造函数
  4. 实例化ToMock,实例 B ,绑定到实例 A ,并在UnderTest singleton
  5. 中注册
  6. 实例化SomeTest,实例 C
  7. 在实例 C
  8. 上调用构造函数
  9. 实例化ToMock,实例 D ,绑定到实例 C ,并在UnderTest单例中注册,覆盖其中的内容前
  10. 在实例 A
  11. 上调用test1
  12. set实例 B
  13. 上调用ToMock
  14. doSomething实例D上调用ToMock,因为这是存储在UnderTest单例中的getInstance,通过ToMock
  15. 检索到的

    set实例 D 尚未调用SomeTest。因此,相应的C实例str的{​​{1}}字段仍为null

    使用JUnit 4,我们交替创建一个运行相应测试的实例。在您的测试用例中,UnderTest单例不会与错误的ToMock实例“混淆”。

答案 1 :(得分:1)

设置方法(@Before)和@After注释方法的任何更新都将在方法调用之间保持不变。

 @Before
public void executedBeforeEach() {
....
}

每次调用test方法之前/之后都会调用该调用。如果这还不够,那么请考虑使用@BeforeClass注释。