Spring Boot @WebMvcTest vs @SpringBootTest

huangapple go评论75阅读模式
英文:

Spring Boot @WebMvcTest vs @SpringBootTest

问题

我有一个简单的健康控制器定义如下:

@RestController
@RequestMapping("/admin")
public class AdminController {

    @Value("${spring.application.name}")
    String serviceName;

    @GetMapping("/health")
    String getHealth() {
        return serviceName + " up and running";
    }
}

以及用于测试的测试类:

@WebMvcTest(AdminController.class)
class AdminControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void healthShouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/admin/health"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("live-data-service up and running")));
    }
}

运行测试时,我遇到以下错误:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field configuration in com.XXXX.LiveDataServiceApplication required a bean of type 'com.XXXXX.AppConfiguration' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)

Action:

Consider defining a bean of type 'com.XXXX.AppConfiguration' in your configuration.

这是在与主 Spring Boot 应用类相同包中定义的 AppConfiguration.java

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties
public class AppConfiguration {

    @Value("${redis.host}")
    private String redisHost;

    @Value("${redis.port}")
    private int redisPort;

    @Value("${redis.password:}")
    private String redisPassword;
...
// getters and setters come here

主类:

@SpringBootApplication
public class LiveDataServiceApplication {

    @Autowired
    private AppConfiguration configuration;

    public static void main(String[] args) {
        SpringApplication.run(LiveDataServiceApplication.class, args);
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(configuration.getRedisHost(), configuration.getRedisPort());
        redisConfiguration.setPassword(configuration.getRedisPassword());
        return new LettuceConnectionFactory(redisConfiguration);
    }
}

如果我将测试类中的注释修改如下,测试将通过:

@SpringBootTest
@AutoConfigureMockMvc
class AdminControllerTest {
...

我漏掉了什么?

英文:

I have a simple health controller defined as follows:

@RestController
@RequestMapping("/admin")
public class AdminController {

    @Value("${spring.application.name}")
    String serviceName;

    @GetMapping("/health")
    String getHealth() {
        return serviceName + " up and running";
    }
}

And the test class to test it:

@WebMvcTest(RedisController.class)
class AdminControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void healthShouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/admin/health"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("live-data-service up and running")));
    }
}

When running the test, I'm getting the below error:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field configuration in com.XXXX.LiveDataServiceApplication required a bean of type 'com.XXXXX.AppConfiguration' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.XXXX.AppConfiguration' in your configuration.

Here is AppConfiguration.java defined in the same package as the main spring boot app class:

@Configuration
@EnableConfigurationProperties
@ConfigurationProperties
public class AppConfiguration {

    @Value("${redis.host}")
    private String redisHost;

    @Value("${redis.port}")
    private int redisPort;

    @Value("${redis.password:}")
    private String redisPassword;
...
// getters and setters come here

Main class:

@SpringBootApplication
public class LiveDataServiceApplication {

    @Autowired
    private AppConfiguration configuration;

    public static void main(String[] args) {
        SpringApplication.run(LiveDataServiceApplication.class, args);
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(configuration.getRedisHost(), configuration.getRedisPort());
        redisConfiguration.setPassword(configuration.getRedisPassword());
        return new LettuceConnectionFactory(redisConfiguration);
    }
}

If I modify the annotation in the test class as follows, the test pass:

@SpringBootTest
@AutoConfigureMockMvc
class AdminControllerTest {
....

What am I missing?

答案1

得分: 7

你应该理解 @WebMvcTest@SpringBootTest 的用法。

@WebMvcTest: 这个注解只会实例化Web层,而不是整个上下文,所以控制器类中的所有依赖都应该被模拟。你可以查看文档

>Spring Boot 只会实例化Web层,而不是整个上下文。在一个包含多个控制器的应用中,你甚至可以通过使用 @WebMvcTest(HomeController.class) 来只实例化其中一个。

>我们使用 @MockBean 来创建并注入 GreetingService 的模拟(如果不这样做,应用程序上下文将无法启动)。

@SpringBootTest: SpringBootTest 注解实际上会加载用于测试环境的应用程序上下文。

>@SpringBootTest 注解告诉 Spring Boot 查找一个主要的配置类(比如带有 @SpringBootApplication 的类),并使用它来启动一个 Spring 应用程序上下文。

英文:

You should understand the usage of @WebMvcTest and @SpringBootTest

@WebMvcTest : annotation is only to instantiates only the web layer rather than the whole context, so all dependencies in controller class should be mocked, you can look at the documentation

>Spring Boot instantiates only the web layer rather than the whole context. In an application with multiple controllers, you can even ask for only one to be instantiated by using, for example, @WebMvcTest(HomeController.class).

>We use @MockBean to create and inject a mock for the GreetingService (if you do not do so, the application context cannot start)

SpringBootTest : Spring boot test annotation actual load the application context for test environment

>The @SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with @SpringBootApplication, for instance) and use that to start a Spring application context.

答案2

得分: 0

src/test/resource/application.file中定义所有属性
示例使用JUnit 5进行REST层的单元测试:

@ExtendWith(MockitoExtension.class)
public class RestTest {

    @InjectMocks
    private RestClass restClass;

    private MockMvc mockMvc;

    @BeforeEach
    public void init() throws Exception {
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(restClass).build();
    }

    @Test
    public void test() throws Exception {
        String url = "/url";
        ResultActions resultActions = mockMvc.perform(get(url));
        resultActions.andExpect(status().isOk());
    }
}
英文:

Define all properties in src/test/resource/application.file
example to use junit 5 for rest layer:

@ExtendWith(MockitoExtension.class)
public class RestTest {

	
	@InjectMocks
	private RestClass  restClass;
	
	
	
	private MockMvc mockMvc;
	
	@BeforeEach
	public void init() throws Exception {
		MockitoAnnotations.initMocks(this);
		mockMvc = MockMvcBuilders.standaloneSetup(restClass).build();
	}
	
	@Test
	public void test() throws Exception {
		String url = "/url";
		ResultActions resultActions = mockMvc.perform(get(url));
		resultActions.andExpect(status().isOk());

	}
	}

huangapple
  • 本文由 发表于 2020年8月20日 23:26:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63508359.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定