我可以创建一个 spring bean 作为我的黄瓜步骤定义的一部分吗?
Can I create a spring bean as part of my cucumber step definitions?
对于某些上下文,我的 spring 启动应用程序使用 LocalDate.now()
调用提供当前日期的外部 API 并检索各种信息。我们正在使用 Cucumber 进行测试,上面的内容在编写步骤定义时出现了问题,例如
Given external api "/some/endpoint/2021-04-21" returns csv
| currency |
| GBP |
步骤定义测试代码使用 wiremock 来模拟该调用,但由于我们在生产代码中使用 LocalDate.now()
,因此测试将在 2021-04-21 以外的任何一天失败。
我解决这个问题的方法是为 Clock 定义两个 bean,然后我们可以自动装配到需要它们的服务中并使用 LocalDate.now(clock)
。 “真正的”bean 是这样定义的:
@Configuration
public class ClockConfiguration {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
测试 bean 是这样的:
@Profile("cucumber")
@TestConfiguration
public class ClockConfiguration {
@Bean
public Clock clock() {
return Clock.fixed(Instant.parse("2021-02-26T10:00:00Z"), ZoneId.systemDefault());
}
}
这解决了我的问题并允许我为我的测试设置定义的时间,但我的问题是 date/time 是在测试配置中定义的。我想将其定义为我的步骤定义的一部分。例如像
Given now is "2021-02-26T10:00:00Z"
根据
定义步骤
@Given("now is {string})
public void setDateTime(String dateTime) {
//Create Clock bean here...
}
我有办法做到这一点吗?或者甚至在黄瓜步骤中覆盖现有的 bean?
尝试用变量或静态方法替换日期,步骤定义可以更改其响应。
@Profile("cucumber")
@TestConfiguration
public class ClockConfiguration {
@Bean
public Clock clock() {
return Clock.fixed(Instant.parse(TestScope.getDateValue()), ZoneId.systemDefault());
}
}
@Given("now is {string})
public void setDateTime(String dateTime) {
TestScope.setDateValue(dateTime);
}
我设法找到了解决此问题的两个有效解决方案。第一个有点复杂,但我基本上在 Clock
周围创建了一个包装器 class,因此一个带有方法 Clock getClock()
的接口然后有两个实现。一个是真正的 bean,它只会 return Clock.systemDefaultZone()
和一个具有私有 Clock 成员的测试 bean,getter 只会 return,但最重要的是 setter喜欢
public void setClock(String dateTime) {
this.clock = Clock.fixed(Instant.parse(dateTime), ZoneId.systemDefault());
}
然后我会通过获取 bean 将其称为 setter 作为步骤定义方法的一部分,例如
@Given("now is {string})
public void setDateTime(String dateTime) {
applicationContext.getBean(MyWrapperClock.class).setClock(dateTime);
}
我发现的第二种方法简单得多,就是在测试配置文件中简单地模拟时钟,例如
@Bean
public Clock clock() {
return Mockito.mock(Clock.class);
}
然后将其自动连接到步骤定义中 class 并执行正常的 mockito when
例如
@Given("now is {string})
public void setDateTime(String dateTime) {
when(this.clock.instant()).thenReturn(Instant.parse(dateTime));
when(this.clock.getZone()).thenReturn(ZoneId.systemDefault());
}
对于某些上下文,我的 spring 启动应用程序使用 LocalDate.now()
调用提供当前日期的外部 API 并检索各种信息。我们正在使用 Cucumber 进行测试,上面的内容在编写步骤定义时出现了问题,例如
Given external api "/some/endpoint/2021-04-21" returns csv
| currency |
| GBP |
步骤定义测试代码使用 wiremock 来模拟该调用,但由于我们在生产代码中使用 LocalDate.now()
,因此测试将在 2021-04-21 以外的任何一天失败。
我解决这个问题的方法是为 Clock 定义两个 bean,然后我们可以自动装配到需要它们的服务中并使用 LocalDate.now(clock)
。 “真正的”bean 是这样定义的:
@Configuration
public class ClockConfiguration {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}
测试 bean 是这样的:
@Profile("cucumber")
@TestConfiguration
public class ClockConfiguration {
@Bean
public Clock clock() {
return Clock.fixed(Instant.parse("2021-02-26T10:00:00Z"), ZoneId.systemDefault());
}
}
这解决了我的问题并允许我为我的测试设置定义的时间,但我的问题是 date/time 是在测试配置中定义的。我想将其定义为我的步骤定义的一部分。例如像
Given now is "2021-02-26T10:00:00Z"
根据
定义步骤@Given("now is {string})
public void setDateTime(String dateTime) {
//Create Clock bean here...
}
我有办法做到这一点吗?或者甚至在黄瓜步骤中覆盖现有的 bean?
尝试用变量或静态方法替换日期,步骤定义可以更改其响应。
@Profile("cucumber")
@TestConfiguration
public class ClockConfiguration {
@Bean
public Clock clock() {
return Clock.fixed(Instant.parse(TestScope.getDateValue()), ZoneId.systemDefault());
}
}
@Given("now is {string})
public void setDateTime(String dateTime) {
TestScope.setDateValue(dateTime);
}
我设法找到了解决此问题的两个有效解决方案。第一个有点复杂,但我基本上在 Clock
周围创建了一个包装器 class,因此一个带有方法 Clock getClock()
的接口然后有两个实现。一个是真正的 bean,它只会 return Clock.systemDefaultZone()
和一个具有私有 Clock 成员的测试 bean,getter 只会 return,但最重要的是 setter喜欢
public void setClock(String dateTime) {
this.clock = Clock.fixed(Instant.parse(dateTime), ZoneId.systemDefault());
}
然后我会通过获取 bean 将其称为 setter 作为步骤定义方法的一部分,例如
@Given("now is {string})
public void setDateTime(String dateTime) {
applicationContext.getBean(MyWrapperClock.class).setClock(dateTime);
}
我发现的第二种方法简单得多,就是在测试配置文件中简单地模拟时钟,例如
@Bean
public Clock clock() {
return Mockito.mock(Clock.class);
}
然后将其自动连接到步骤定义中 class 并执行正常的 mockito when
例如
@Given("now is {string})
public void setDateTime(String dateTime) {
when(this.clock.instant()).thenReturn(Instant.parse(dateTime));
when(this.clock.getZone()).thenReturn(ZoneId.systemDefault());
}