测试 Spring 引导命令行应用程序
Testing a Spring Boot command line application
我想测试我的 Spring 引导命令行应用程序。我想模拟某些 bean(我可以通过在测试 class 的顶部注释 @ContextConfiguration(classes = TestConfig.class)
来做到这一点。在 TestConfig.class
中,我覆盖了我想模拟的 bean 。我想 Spring Boot
找到其余的组件。这似乎可行。
问题是当我运行 测试时,整个应用程序正常启动(即调用run()
方法)。
@Component
public class MyRunner implements CommandLineRunner {
//fields
@Autowired
public MyRunner(Bean1 bean1, Bean2 bean2) {
// constructor code
}
@Override
public void run(String... args) throws Exception {
// run method implementation
}
我试图覆盖 MyRunner
@Bean
并将其放入 TestConfig.class
,但这似乎不起作用。我知道我正在加载常规应用程序上下文,但这就是我想做的(我想?)因为我想重新使用我在我的应用程序,并且只模拟一小部分。
有什么建议吗?
编辑:
Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
CommandLineRunners
是普通bean,有一个例外:
加载应用程序上下文后,spring boot 在其所有 bean 中查找实现此接口的 bean 并自动调用它们的 run
方法。
现在,我想请您执行以下操作:
- 从测试中删除
ContextConfiguration
并在 MyRunner
的构造函数中放置一个断点。测试应如下所示:
@RunWith(SpringRunner.class) // if you're on junit 4, adjust for junit 5 if you need
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyTest {
@Autowired
private MyRunner myRunner;
@Test
public void testMe() {
System.out.println("hello");
}
}
- 运行 测试并确保我的运行ner 已加载并且它的
run
方法被调用
- 现在用 MockBean 注释模拟这个 class:
@RunWith(SpringRunner.class) // if you're on junit 4, adjust for junit 5 if you need
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyTest {
@MockBean
private MyRunner myRunner;
@Test
public void testMe() {
System.out.println("hello");
}
}
运行 测试。确保 run
方法不是 运行ning。您的应用程序上下文现在应该包含组件的模拟实现。
如果上述方法有效,则问题出在 TestConfig
和 ContextConfiguration
注释上。一般来说,当你 运行 没有 ContextConfiguration
时,你给 spring 启动测试引擎一个自由来模仿应用程序上下文启动,就好像它是一个真正的应用程序(具有自动配置,属性 分辨率,递归 bean 扫描等)。
但是,如果您放置 ContextConfiguration,spring 启动测试不会像这样工作 - 相反它只会加载您在该配置中指定的 bean。没有自动配置,例如没有递归 bean 扫描。
更新
根据 OP 的评论:
看起来 MyRunner
在您放置 @ContextConfiguration 时加载,因为组件扫描。由于您在 MyRunner
class 上放置了注释 @Component
,Spring 引导引擎可以发现它。
事实上这里有两种类型的 beans 定义的混合 "dangerous":
1、在@Configuration
注解中用@Bean
定义的bean
2.组件扫描发现的bean
这里有一个问题要问你:如果你不想模仿应用程序的启动过程,而是更喜欢只加载特定的 bean,那你为什么要使用 @SpringBootTest
?也许您可以通过以下方式实现目标:
@RunWith(SpringRunner.class)
@ContextConfiguration(YourConfig.class)
public class MyTest {
...
}
您可以这样做的一种方法是使用 main 方法有 2 个 classes,一个设置 "normal" 上下文,另一个设置 "mock" 上下文:
普通应用程序上下文,使用通常的 Application
@SpringBootApplication(scanBasePackages = "com.example.demo.api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Foo foo() {
return new Foo("I am not mocked");
}
@Bean
public Bar bar() {
return new Bar("this is never mocked");
}
}
添加另一个 Application
class 用模拟的
覆盖正常上下文
@SpringBootApplication(scanBasePackageClasses = {MockApplication.class, Application.class})
@Component
public class MockApplication {
public static void main(String[] args) {
SpringApplication.run(MockApplication.class, args);
}
@Bean
public Foo foo() {
return new Foo("I am mocked");
}
}
当你 运行 Application.main
Foo 将是 "I am not mocked",当你 运行 MockApplication.main()
它将是 "I am mocked"
答案比我想象的要简单。在
中添加 MockBean
@TestConfiguration
public class TestConfig {
@MockBean
private MyRunner myRunner;
}
我们可以使用@MockBean 将模拟对象添加到Spring 应用程序上下文。模拟将替换应用程序上下文中相同类型的任何现有 bean。
所以 MyRunner.run()
永远不会被调用,但我仍然可以在我的应用程序中使用所有其他 bean。
我想测试我的 Spring 引导命令行应用程序。我想模拟某些 bean(我可以通过在测试 class 的顶部注释 @ContextConfiguration(classes = TestConfig.class)
来做到这一点。在 TestConfig.class
中,我覆盖了我想模拟的 bean 。我想 Spring Boot
找到其余的组件。这似乎可行。
问题是当我运行 测试时,整个应用程序正常启动(即调用run()
方法)。
@Component
public class MyRunner implements CommandLineRunner {
//fields
@Autowired
public MyRunner(Bean1 bean1, Bean2 bean2) {
// constructor code
}
@Override
public void run(String... args) throws Exception {
// run method implementation
}
我试图覆盖 MyRunner
@Bean
并将其放入 TestConfig.class
,但这似乎不起作用。我知道我正在加载常规应用程序上下文,但这就是我想做的(我想?)因为我想重新使用我在我的应用程序,并且只模拟一小部分。
有什么建议吗?
编辑:
Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
CommandLineRunners
是普通bean,有一个例外:
加载应用程序上下文后,spring boot 在其所有 bean 中查找实现此接口的 bean 并自动调用它们的 run
方法。
现在,我想请您执行以下操作:
- 从测试中删除
ContextConfiguration
并在MyRunner
的构造函数中放置一个断点。测试应如下所示:
@RunWith(SpringRunner.class) // if you're on junit 4, adjust for junit 5 if you need
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyTest {
@Autowired
private MyRunner myRunner;
@Test
public void testMe() {
System.out.println("hello");
}
}
- 运行 测试并确保我的运行ner 已加载并且它的
run
方法被调用 - 现在用 MockBean 注释模拟这个 class:
@RunWith(SpringRunner.class) // if you're on junit 4, adjust for junit 5 if you need
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyTest {
@MockBean
private MyRunner myRunner;
@Test
public void testMe() {
System.out.println("hello");
}
}
运行 测试。确保
run
方法不是 运行ning。您的应用程序上下文现在应该包含组件的模拟实现。如果上述方法有效,则问题出在
TestConfig
和ContextConfiguration
注释上。一般来说,当你 运行 没有ContextConfiguration
时,你给 spring 启动测试引擎一个自由来模仿应用程序上下文启动,就好像它是一个真正的应用程序(具有自动配置,属性 分辨率,递归 bean 扫描等)。 但是,如果您放置 ContextConfiguration,spring 启动测试不会像这样工作 - 相反它只会加载您在该配置中指定的 bean。没有自动配置,例如没有递归 bean 扫描。
更新
根据 OP 的评论:
看起来 MyRunner
在您放置 @ContextConfiguration 时加载,因为组件扫描。由于您在 MyRunner
class 上放置了注释 @Component
,Spring 引导引擎可以发现它。
事实上这里有两种类型的 beans 定义的混合 "dangerous":
1、在@Configuration
注解中用@Bean
定义的bean
2.组件扫描发现的bean
这里有一个问题要问你:如果你不想模仿应用程序的启动过程,而是更喜欢只加载特定的 bean,那你为什么要使用 @SpringBootTest
?也许您可以通过以下方式实现目标:
@RunWith(SpringRunner.class)
@ContextConfiguration(YourConfig.class)
public class MyTest {
...
}
您可以这样做的一种方法是使用 main 方法有 2 个 classes,一个设置 "normal" 上下文,另一个设置 "mock" 上下文:
普通应用程序上下文,使用通常的 Application
@SpringBootApplication(scanBasePackages = "com.example.demo.api")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public Foo foo() {
return new Foo("I am not mocked");
}
@Bean
public Bar bar() {
return new Bar("this is never mocked");
}
}
添加另一个 Application
class 用模拟的
@SpringBootApplication(scanBasePackageClasses = {MockApplication.class, Application.class})
@Component
public class MockApplication {
public static void main(String[] args) {
SpringApplication.run(MockApplication.class, args);
}
@Bean
public Foo foo() {
return new Foo("I am mocked");
}
}
当你 运行 Application.main
Foo 将是 "I am not mocked",当你 运行 MockApplication.main()
它将是 "I am mocked"
答案比我想象的要简单。在
中添加 MockBean@TestConfiguration
public class TestConfig {
@MockBean
private MyRunner myRunner;
}
我们可以使用@MockBean 将模拟对象添加到Spring 应用程序上下文。模拟将替换应用程序上下文中相同类型的任何现有 bean。
所以 MyRunner.run()
永远不会被调用,但我仍然可以在我的应用程序中使用所有其他 bean。