我可以使用 mockito 来匹配具有自动更新时间戳的对象吗? [英] Can I use mockito to match an object with an auto updated timestamp?

查看:22
本文介绍了我可以使用 mockito 来匹配具有自动更新时间戳的对象吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在进行模拟调用之前自动更新时间戳的最佳方法是什么?

What would be the best way to something like this where a timestamp is updated automatically before making the mocked call?

这是我要测试的一些虚拟代码:

Here is some dummy code of what I am trying to test:

public class ThingWithATimestamp {
    public Long timestamp;
    public String name;

    public ThingWithATimestamp(String name) {
        this.name = name;
    }
}

public class TheClassThatDoesStuff {
    private ThingConnector connector;

    public TheClassThatDoesStuff(ThingConnector connector) {
        this.connector = connector;
    }

    public void updateTheThing(MyThingWithATimestamp thing) {
        thing.timestamp = currentTimestamp();
        connector.update(thing);            
    }
}

这是我要测试的内容:

public class TheClassThatDoesStuffTests {
    @Test
    public void canUpdateTheThing() {
        ThingConnector connector = mock(ThingConnector.class);
        TheClassThatDoesStuff doer = new ThisClassThatDoesStuff(connector);

        doer.updateTheThing(new ThingWithATimestamp("the name"));

        verify(connector, times(1)).update(SomeMatcherThatICantFigureOut);
    }

我知道这段代码相当笨拙,但我认为它准确地描绘了我想要验证的内容.我基本上需要一个匹配器来填写测试以验证时间戳是否在当前时间的 X 之内,所以我知道它已正确更新并且 connector.update 是使用对象的正确时间戳调用的.

I know this code is pretty dumbed down but I think it accurately portrays what I am trying to verify. I basically need a matcher to fill in the test to verify that the timestamp is within X of the current time so I know it got updated correctly and that connector.update was called with the proper timestamp for the object.

推荐答案

我发现处理对时间要求严格的代码最可靠的方法是将所有对时间要求严格的函数都包含在它们自己的类中.我通常称之为TimeHelper.所以这个类可能如下所示.

I find the most robust way to deal with time-critical code is to wrap up all of your time-critical functions in their own class. I usually call it TimeHelper. So this class might look like the following.

import java.util.Date;
public class TimeHelper{
    public long currentTimeMillis(){
        return System.currentTimeMillis();
    }
    public Date makeDate(){
        return new Date();
    }
}

并且它可能有更多相同类型的方法.现在,任何使用此类函数的类都应该(至少)有两个构造函数——您将在应用程序中使用的普通构造函数,以及一个包私有的构造函数,其中 TimeHelper 是一个参数.这个 TimeHelper 需要保存起来以备后用.

and it might have more methods of the same type. Now, any class that uses such functions should have (at least) two constructors - the normal one that you'll use in your application, plus a package-private one in which a TimeHelper is a parameter. This TimeHelper needs to be stored away for later use.

public class ClassThatDoesStuff {
    private ThingConnector connector;
    private TimeHelper timeHelper;

    public ClassThatDoesStuff(ThingConnector connector) {
        this(connector, new TimeHelper());
    }

    ClassThatDoesStuff(ThingConnector connector, TimeHelper timeHelper) {
        this.connector = connector;
        this.timeHelper = timeHelper;
    } 
}

现在,在您的类中,不要编写 System.currentTimeMillis(),而是编写 timeHelper.currentTimeMillis().当然,这将产生完全相同的效果;除了现在,你的类神奇地变得更易于测试了.

Now, within your class, instead of writing System.currentTimeMillis(), write timeHelper.currentTimeMillis(). This will, of course, have exactly the same effect; except now, your class has magically become much more testable.

当你测试你的类时,模拟 TimeHelper.配置此模拟(使用 Mockito 的 whenthenReturn,或者 doReturn)以返回您喜欢的任何时间值 - 无论您需要什么.如果您要在测试过程中多次调用 currentTimeMillis(),您甚至可以在此处返回多个值.

When you test your class, make a mock of TimeHelper. Configure this mock (using Mockito's when and thenReturn, or alternatively doReturn) to return any time values you like - whatever you need for your test. You can even return multiple values here, if you're going to have multiple calls to currentTimeMillis() in the course of the test.

现在使用第二个构造函数创建您要测试的对象,并传入模拟.这使您可以完美地控制测试中将使用的时间值;并且您可以进行断言或验证,以断言使用了正确的值.

Now use the second constructor to make the object that you're going to test, and pass in the mock. This gives you perfect control of what time values will be used in the test; and you can make your assertions or verifications assert that precisely the right value has been used.

public class ClassThatDoesStuffTest{
    @Mock private TimeHelper mockTime;
    @Mock private ThingConnector mockConnector;
    private ClassThatDoesStuff toTest;

    @Test
    public void doesSomething(){
        // Arrange
        initMocks(this);
        when(mockTime.currentTimeMillis()).thenReturn(1000L, 2000L, 5000L);
        toTest = new ClassThatDoesStuff(mockConnector, mockTime);

        // Act
        toTest.doSomething();

        // Assert
        // ... ???
    }
}        

如果您这样做,您就知道您的测试将始终有效,并且永远不会依赖于您的操作系统的时间切片策略.您还可以验证时间戳的确切值,而不是断言它们在某个近似区间内.

If you do this, you know that your test will always work, and never be dependent on the time slicing policies of your operating system. You also have the power to verify the exact values of your timestamps, rather than asserting that they fall within some approximate interval.

这篇关于我可以使用 mockito 来匹配具有自动更新时间戳的对象吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆