集成测试API的行为取决于当前时间

时间:2017-09-22 15:18:31

标签: java rest testing time integration-testing

我有一些API我尝试彻底 集成 测试 - 我正在尝试在测试环境中运行的远程服务(不是运行测试的同一个框,测试进行真正的服务调用),我不能只使用依赖注入来解决我的所有问题。我们已经对这些类进行了一系列良好的单元测试。

我测试的API是SaveThingy。如果它有效,它会保存一个Thingy,并返回它的id。其中一项检查是,您只能在某些时间保存,只能在工作日保存。如果你在周末打电话给SaveThingy,它会侮辱你,而不是保存一个Thingy。实现类似于以下

ThingyId saveThingy(Thingy thingy) {
    if (isWeekend(LocalDate.now().getDayOfWeek())) {
        throw new PersonalInsultException("your mother wears army boots");
    }
    return thingyDao.save(thingy);
}

理想情况下,我希望每次运行集成测试时都会测试这两种情况,而不需要等待。在某些代码中,我希望每次都运行类似于以下的测试。

@Test
public void saveThingy_validThingyOnWeekday_savesThingy() {
    ThingyId id = serviceUnderTest.saveThingy(THINGY);
    assertThat(serviceUnderTest.getThingyById(id)).isEqualTo(THINGY);
}

@Test(expected = PersonalInsultException.class)
public void saveThingy_validThingyOnWeekend_receivePersonalInsult() {
    serviceUnderTest.saveThing(THINGY);
}

是否有任何标准方法可以完全测试此类API?我考虑了一些选项(下面),但想获得更多意见。

  1. 拒绝集成测试,只使用这些API的单元测试
  2. 更改远程时钟,使用私有API或在运行每个测试之前逐字地插入主机
  3. 编写与时间相关的测试;只测试一种可能的行为,或测试一种行为,然后睡觉直到满足其他条件
  4. 发明将始终保存或始终抛出异常的虚拟数据

3 个答案:

答案 0 :(得分:2)

我想在你的ThingyService课程中,你有一个公众 或受保护的isWeekend方法。可能是这样的:

public boolean isWeekend(DayOfWeek dayOfWeek) {
    return dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY;
}    

ThingyServiceTest中,您可以使用模拟的ThingyService方法创建两个专门的isWeekend个实例。 在您的测试用例中,您可以使用以下任一方法:

// service with weekday behavior
private ThingyService serviceUnderTest_weekday = new ThingyService() {
    @Override
    public boolean isWeekend(DayOfWeek dayOfWeek) {
        return false;
    }
};

// service with weekend behavior
private ThingyService serviceUnderTest_weekend = new ThingyService() {
    @Override
    public boolean isWeekend(DayOfWeek dayOfWeek) {
        return true;
    }
};

@Test
public void saveThingy_validThingyOnWeekday_savesThingy() {
    ThingyId id = serviceUnderTest_weekday.saveThingy(THINGY);
    assertThat(serviceUnderTest_weekday.getThingyById(id)).isEqualTo(THINGY);
}

@Test(expected = PersonalInsultException.class)
public void saveThingy_validThingyOnWeekend_receivePersonalInsult() {
    serviceUnderTest_weekend.saveThing(THINGY);
}

答案 1 :(得分:1)

您正在尝试使用白盒测试要求进行黑盒测试:这根本不可能。坚持白盒测试并在本地模拟你的DateProvider。也许不是你想听到的,但是你会浪费那么多时间,否则你会试图调整星星以产生你想要的输出。

另一方面,如果您真的想这样做,请在Docker容器中运行您的应用程序并更改系统时钟,然后在测试之间将其拆除。

或者打开一个单独的端点,允许通过您的服务指定当前时间,并在测试时仅部署此端点。或者有一个配置界面负责确定当前时间的来源,以及何时部署到测试环境,相应地配置应用程序。

答案 2 :(得分:1)

不要使用LocalDate.now()Instant.now()Whatever.now()或传递long个时间戳,请考虑在您的类中添加java.time.Clock字段,并添加初始化参数构造函数或提供setter。

Clock.instant()基本上是Instant.now(),您可以将该广告转换为任何其他时态类。

在制作中,您将使用Clock.systemUTC()作为该字段的值。 在某些测试中,您可以使用Clock.fixed(),在所有测试中,您可以模拟测试所需的方式。

这种方法主要适用于单元测试。您仍然可以在集成环境中注入自定义Clock实现,这样整个应用程序就会认为当前时间是您需要的。