Mockito 在测试中途验证状态

发布于 2024-11-18 17:24:13 字数 1032 浏览 3 评论 0原文

我有一些代码,简单地说,将对象设置为“正在处理”状态,执行一些操作,然后将其设置为“成功”。我想验证处理保存是否使用正确的值完成。

问题是,当执行 verify() 测试时,会在测试结束时(而不是中途)对对象调用 .equals()

例如代码:

public void process(Thing thing) {
    thing.setValue(17);
    thing.setStatus(Status.PROCESSING);
    dao.save(thing);

    doSomeMajorProcessing(thing);

    thing.setStatus(Status.SUCCESS);
    dao.save(thing);
}

我想测试:

public void test() {
    Thing actual = new Thing();
    processor.process(actual);

    Thing expected = new Thing();
    expected.setValue(17);
    expected.setStatus(Status.PROCESSING);
    verify(dao).save(expected);

    // ....

    expected.setStatus(Status.SUCCESS);
    verify(dao).save(expected);
}

在第一次验证时,actual.getStatus()Status.SUCCESS,因为Mockito只保留对对象的引用,并且可以仅在最后测试其值。

我考虑过,如果涉及 when(...) ,那么 .equals() 将在正确的时间被调用,并且结果只有在 Thing 是什么时才会发生我希望如此。但是,在这种情况下 .save() 不返回任何内容。

如何验证对象是否处于正确的状态?

I have some code that put simply, sets an object to a state of PROCESSING, does some stuff, then sets it to SUCCESS. I want to verify that the PROCESSING save is done with the correct values.

The problem is when the verify() tests are performed, .equals() is called on the object as it is at the end of the test, rather than halfway through.

For example the code:

public void process(Thing thing) {
    thing.setValue(17);
    thing.setStatus(Status.PROCESSING);
    dao.save(thing);

    doSomeMajorProcessing(thing);

    thing.setStatus(Status.SUCCESS);
    dao.save(thing);
}

I want to test:

public void test() {
    Thing actual = new Thing();
    processor.process(actual);

    Thing expected = new Thing();
    expected.setValue(17);
    expected.setStatus(Status.PROCESSING);
    verify(dao).save(expected);

    // ....

    expected.setStatus(Status.SUCCESS);
    verify(dao).save(expected);
}

On the first verify, actual.getStatus() is Status.SUCCESS, as Mockito just keeps a reference to the object and can only test it's value at the end.

I have considered that if a when(...) where involved then .equals() would be called at the correct time and the result would only happen if Thing was what I wanted it to be. However, in this case .save() returns nothing.

How can I verify that the object is put into the correct states?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

请止步禁区 2024-11-25 17:24:14

查看 https://code.google.com/archive/p/mockito/ 后问题/126
我能够让我的版本工作(Java 15,Mockito 3.6.28):

// ========= CODE ==========
public void process(Thing thing) {
    thing.setValue(17);
    thing.setStatus(Status.PROCESSING);
    dao.save(thing);

    doSomeMajorProcessing(thing);

    thing.setStatus(Status.SUCCESS);
    dao.save(thing);
}


// ========= TEST ==========

// ++++++ NOTE - put this at class level 

private final Dao dao = mock(Dao.class, withSettings().defaultAnswer(new ClonesArguments()));

public void test() {
    Thing actual = new Thing();
    processor.process(actual);

    ArgumentCaptor<Thing> captor = ArgumentCaptor.for(Thing.class);
    verify(dao, times(2)).save(captor.capture());
    List<Things> savedCalls = captor.getAllValues();
    assertEquals(Status.PROCESSING, savedCalls.get(0).getStatus());
    assertEquals(Status.SUCCESS, savedCalls.get(1).getStatus());
}

After reviewing https://code.google.com/archive/p/mockito/issues/126
I was able to get my version of this working (Java 15, Mockito 3.6.28):

// ========= CODE ==========
public void process(Thing thing) {
    thing.setValue(17);
    thing.setStatus(Status.PROCESSING);
    dao.save(thing);

    doSomeMajorProcessing(thing);

    thing.setStatus(Status.SUCCESS);
    dao.save(thing);
}


// ========= TEST ==========

// ++++++ NOTE - put this at class level 

private final Dao dao = mock(Dao.class, withSettings().defaultAnswer(new ClonesArguments()));

public void test() {
    Thing actual = new Thing();
    processor.process(actual);

    ArgumentCaptor<Thing> captor = ArgumentCaptor.for(Thing.class);
    verify(dao, times(2)).save(captor.capture());
    List<Things> savedCalls = captor.getAllValues();
    assertEquals(Status.PROCESSING, savedCalls.get(0).getStatus());
    assertEquals(Status.SUCCESS, savedCalls.get(1).getStatus());
}
萤火眠眠 2024-11-25 17:24:14

使用 argThat< /a> 带有 hamcrest Matcher 应该可以解决问题。如果事物具有 PROCESSING 状态,则匹配器将匹配其传递的事物

public class ProcessingMatcher extends BaseMatcher<Thing> {
    @Override
    public boolean matches(Object item) {
        if (item instanceof Thing) {
            return ((Thing) item).getStatus() == Status.PROCESSING;
        }
        return false;
    }

    @Override
    public void describeTo(Description description) {
        description.appendText(" has the PROCESSING status");
    }
}

然后在您的测试中,使用以下代码:

public class MyTest {

    public void test() {
        //...
        mockDao.save(argThat(hasTheProcessingStatus()));
    }

    static ProcessingMatcher hasTheProcessingStatus() {
        return new ProcessingMatcher();
    }
}

Using argThat with a hamcrest Matcher should do the trick. The Matcher would match its passed thing if the thing has the PROCESSING status:

public class ProcessingMatcher extends BaseMatcher<Thing> {
    @Override
    public boolean matches(Object item) {
        if (item instanceof Thing) {
            return ((Thing) item).getStatus() == Status.PROCESSING;
        }
        return false;
    }

    @Override
    public void describeTo(Description description) {
        description.appendText(" has the PROCESSING status");
    }
}

And then in your test, use the following code :

public class MyTest {

    public void test() {
        //...
        mockDao.save(argThat(hasTheProcessingStatus()));
    }

    static ProcessingMatcher hasTheProcessingStatus() {
        return new ProcessingMatcher();
    }
}
捂风挽笑 2024-11-25 17:24:13

好吧,我找到了解决方案,但这非常可怕。验证对我来说没有好处,因为它运行得太晚,并且存根很困难,因为该方法返回一个空值。
但我能做的是存根,如果调用了预期以外的任何内容,则抛出异常,同时验证调用了某些内容:

public void test() {
    Thing actual = new Thing();

    Thing expected = new Thing();
    expected.setValue(17);
    expected.setStatus(Status.PROCESSING);

    doThrow(new RuntimeException("save called with wrong object"))
            .when(dao).saveOne(not(expected));

    processor.process(actual);

    verify(dao).saveOne(any(Thing.class));

    // ....

    expected.setStatus(Status.SUCCESS);
    verify(dao).saveTwo(expected);
}

private <T> T not(final T p) {
    return argThat(new ArgumentMatcher<T>() {
        @Override
        public boolean matches(Object arg) {
            return !arg.equals(p);
        }
    });
}

这推断预期被调用了。唯一的缺点是很难两次验证该方法,但幸运的是,在我的例子中,两个 DAO 调用都是针对不同的方法,因此我可以分别验证它们。

Ok, I found a solution, but it's pretty horrible. Verify is no good to me because it runs too late, and stubbing is hard because the method returns a void.
But what I can do is stub and throw an exception if anything but the expected is called, while validating that something is called:

public void test() {
    Thing actual = new Thing();

    Thing expected = new Thing();
    expected.setValue(17);
    expected.setStatus(Status.PROCESSING);

    doThrow(new RuntimeException("save called with wrong object"))
            .when(dao).saveOne(not(expected));

    processor.process(actual);

    verify(dao).saveOne(any(Thing.class));

    // ....

    expected.setStatus(Status.SUCCESS);
    verify(dao).saveTwo(expected);
}

private <T> T not(final T p) {
    return argThat(new ArgumentMatcher<T>() {
        @Override
        public boolean matches(Object arg) {
            return !arg.equals(p);
        }
    });
}

This infers that expected is called. Only drawback is that it'll be difficult to verify the method twice, but luckily in my case both DAO calls are to different methods, so I can verify them separately.

花期渐远 2024-11-25 17:24:13

为什么不直接嘲笑事物本身并验证它呢?例如:

public class ProcessorTest {

    @Mock
    private Dao mockDao;
    @InjectMocks
    private Processor processor;

    @BeforeMethod   
    public void beforeMethod() {
        initMocks(this);
    }

    public void test() {

        Thing mockThing = Mockito.mock(Thing.class);
        processor.process(thing);

        verify(mockThing).setStatus(Status.PROCESSING);
        verify(mockThing).setValue(17);
        verify(mockDao).save(mockThing);
        verify(mockThing).setStatus(Status.SUCCESS);
    }

如果您想显式测试这些事情发生的顺序,请使用 InOrder 对象:

public void inOrderTest() {

    Thing mockThing = Mockito.mock(Thing.class);
    InOrder inOrder = Mockito.inOrder(mockThing, mockDao);

    processor.process(mockThing);

    inorder.verify(mockThing).setStatus(Status.PROCESSING);
    inorder.verify(mockThing).setValue(17);
    inorder.verify(mockDao).save(mockThing);
    inorder.verify(mockThing).setStatus(Status.SUCCESS);
    inorder.verify(mockDao).save(mockThing);
}

Why not just mock the Thing itself and verify that? eg:

public class ProcessorTest {

    @Mock
    private Dao mockDao;
    @InjectMocks
    private Processor processor;

    @BeforeMethod   
    public void beforeMethod() {
        initMocks(this);
    }

    public void test() {

        Thing mockThing = Mockito.mock(Thing.class);
        processor.process(thing);

        verify(mockThing).setStatus(Status.PROCESSING);
        verify(mockThing).setValue(17);
        verify(mockDao).save(mockThing);
        verify(mockThing).setStatus(Status.SUCCESS);
    }

If you want to explicitly test the order in which these things happen, use an InOrder object:

public void inOrderTest() {

    Thing mockThing = Mockito.mock(Thing.class);
    InOrder inOrder = Mockito.inOrder(mockThing, mockDao);

    processor.process(mockThing);

    inorder.verify(mockThing).setStatus(Status.PROCESSING);
    inorder.verify(mockThing).setValue(17);
    inorder.verify(mockDao).save(mockThing);
    inorder.verify(mockThing).setStatus(Status.SUCCESS);
    inorder.verify(mockDao).save(mockThing);
}
﹂绝世的画 2024-11-25 17:24:13

Mockito 在验证可变对象时存在问题。关于此有一个未解决的问题(http://code.google.com/p/mockito/issues/detail?id=126)

也许您应该切换到 EasyMock。他们使用记录/回放模式,并在调用时进行验证,这与 Mockito 不同,Mockito 是在调用后进行验证。

这个 Mockito 版本的测试存在上述问题:

@Test
public void testMockito() {
    Processor processor = new Processor();
    Dao dao = Mockito.mock(Dao.class);
    processor.setDao(dao);

    Thing actual = new Thing();
    actual.setValue(17);
    processor.process(actual);

    Thing expected1 = new Thing();
    expected1.setValue(17);
    expected1.setStatus(Status.PROCESSING);
    verify(dao).save(expected1);

    Thing expected2 = new Thing();
    expected2.setValue(19);
    expected2.setStatus(Status.SUCCESS);
    verify(dao).save(expected2);
}

这个 EasyMock 版本工作正常:

@Test
public void testEasymock() {
    Processor processor = new Processor();
    Dao dao = EasyMock.createStrictMock(Dao.class);
    processor.setDao(dao);

    Thing expected1 = new Thing();
    expected1.setValue(17);
    expected1.setStatus(Status.PROCESSING);
    dao.save(expected1);

    Thing expected2 = new Thing();
    expected2.setValue(19);
    expected2.setStatus(Status.SUCCESS);
    dao.save(expected2);

    EasyMock.replay(dao);

    Thing actual = new Thing();
    actual.setValue(17);
    processor.process(actual);

    EasyMock.verify(dao);
}

在我的示例中,doSomeMajorProcessingvalue 设置为 19。

private void doSomeMajorProcessing(Thing thing) {
    thing.setValue(19);     
}

Mockito has a problem verifying mutable objects. There is an open issue about this (http://code.google.com/p/mockito/issues/detail?id=126)

Maybe you should switch to EasyMock. They use a record/playback pattern and do the verification at the time of the call in contrary to Mockito, where the verification happens after the call.

This Mockito version of the test has the mentioned problem:

@Test
public void testMockito() {
    Processor processor = new Processor();
    Dao dao = Mockito.mock(Dao.class);
    processor.setDao(dao);

    Thing actual = new Thing();
    actual.setValue(17);
    processor.process(actual);

    Thing expected1 = new Thing();
    expected1.setValue(17);
    expected1.setStatus(Status.PROCESSING);
    verify(dao).save(expected1);

    Thing expected2 = new Thing();
    expected2.setValue(19);
    expected2.setStatus(Status.SUCCESS);
    verify(dao).save(expected2);
}

This EasyMock version works fine:

@Test
public void testEasymock() {
    Processor processor = new Processor();
    Dao dao = EasyMock.createStrictMock(Dao.class);
    processor.setDao(dao);

    Thing expected1 = new Thing();
    expected1.setValue(17);
    expected1.setStatus(Status.PROCESSING);
    dao.save(expected1);

    Thing expected2 = new Thing();
    expected2.setValue(19);
    expected2.setStatus(Status.SUCCESS);
    dao.save(expected2);

    EasyMock.replay(dao);

    Thing actual = new Thing();
    actual.setValue(17);
    processor.process(actual);

    EasyMock.verify(dao);
}

In my example doSomeMajorProcessing sets value to 19.

private void doSomeMajorProcessing(Thing thing) {
    thing.setValue(19);     
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文