【问题标题】:In Grails, is there a good way to mock the current time using Joda time?在 Grails 中,有没有使用 Joda 时间模拟当前时间的好方法?
【发布时间】:2011-09-02 02:48:02
【问题描述】:

我正在编写一些针对当前时间进行日期和时间计算的代码。在 Joda 时代,这是通过(Java)构造函数访问的,因为它是一个不可变对象。我需要能够模拟,以便new DateTime() 返回一个特定的常量瞬间,以便我可以进行合理的测试断言,但不要理会所有其他 DateTime 方法。

事实证明这很糟糕。 Grails mockFor(DateTime, true) 不会让我模拟 Java 构造函数,但是在 Joda 时间中没有明显或可读的非构造函数方法来获取时间。

唯一可用的选项似乎涉及低级 JVM 技术,如 JMockit 或 EasyMock 3 类模拟,这是 Grails 的一个痛点。有没有简单/直接的方法来实现这一目标?

【问题讨论】:

    标签: grails time groovy mocking jodatime


    【解决方案1】:

    我知道这已被接受,但使用 Joda-time,您可以冻结并将其设置为您喜欢的任何内容。所以你可以冻结时间,提前时间,倒退时间。只要您始终使用 Joda,您的对象将获得“现在”,就像您设置的任何时间一样。

    // Stop time (and set a particular point in time):
    DateTimeUtils.setCurrentMillisFixed(whenever);
    
    // Advance time by the offset:
    DateTimeUtils.setCurrentMillisOffset(offsetFromCurrent);
    
    // Restore time (you could do this in an @After method)
    DateTimeUtils.setCurrentMillisSystem();
    

    【讨论】:

    • 哦,太好了,我一直希望有这样的东西。这将使测试更容易。很棒的提示!
    • 现在我觉得自己很愚蠢,因为我们在 Joda。这是一个更好的方法。很好,我学到了一些东西。
    【解决方案2】:

    我们最终使用now() 方法创建了dateService。在单元测试中,我们用

    模拟它
    domainInstance.dateService = [ now: { currentTime } ] as DateService
    

    其中currentTime 是单元测试类字段。这使每个人都依赖dateService(我们唯一的近乎全局的依赖),而对于src 类,必须手动传递它。

    OTOH 的单元测试看起来很清楚。

    【讨论】:

    • 很好的答案。我一直希望避免以不同的方式构建系统只是为了使这个测试成为可能,但我学到了很多关于模拟构造函数的痛苦。
    • 谢谢。我尽量避免直接调用构造函数——它违反了“接口代码”,创建的实例不能像在测试中那样被外部化,有时会导致Construction Blobs。所以它归结为一种依赖注入的形式。
    【解决方案3】:

    您可以使用良好的老式 OO 原则,例如

      interface TimeService {
        DateTime getCurrentTime()
    
        // other time-related methods
      }
    
      class JodaTimeService implements TimeService {
        DateTime getCurrentTime() {
          new DateTime()
        }  
      }
    
      class MockTimeService implements TimeService {
        DateTime getCurrentTime() {
          // return a fixed point in time
        }  
      }
    

    您的代码应该通过依赖注入获得对TimeService 实现的引用。在resources.groovy 中,仅在运行测试时使用MockTimeService

    import grails.util.Environment
    
    beans = {    
        if (Environment.current == Environment.TEST) {
            timeService(MockTimeService)
    
        } else {
             timeService(JodaTimeService)
        }
    }
    

    【讨论】:

    • 独立地采用与上述相同的方法,非常感谢,并且深入研究了一个好的解决方案。并特别感谢有关使用测试环境切换的提示。
    猜你喜欢
    • 1970-01-01
    • 2020-03-27
    • 1970-01-01
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多