如何测试使用 JDBC 的 Spring 控制器?
How can I test a Spring controller that uses JDBC?
我有一个 Spring 控制器,如下所示:
@RestController
@RequestMapping("/foo")
public class FooController {
@Autowired
private NamedParameterJdbcTemplate template;
@GetMapping("/{name}")
public List<Foo> byName(@PathVariable String name) {
Map<String, String> params = new HashMap<>();
params.put("name", name);
List<Foo> result = template.query("SELECT * FROM FOOS WHERE FOO_NM = :name", params, new FooRowMapper());
if (result.size() == 0) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, String.format("foo %s not found", name));
}
return result;
}
}
但是,我不确定如何测试它。我可以做一个基本的“Spring 可以设置”测试:
@SpringBootTest
public class FooControllerTest {
@Autowired
private FooController controller;
@Test
public void canCreate() {
Assertions.assertNotNull(controller);
}
}
但我不确定正确的测试方法是什么,例如 byName
方法。我需要嘲笑什么吗?我可以像普通 Java 方法一样测试它(使用任何参数调用它并断言输出)吗?
您可以像常规 Java 方法一样对其进行测试,或者使用 MockMvc 对相关路径执行获取请求。
如果你把你的方法的主体贴出来就好了(现在很难帮忙)。
我根本不是测试控制器的最佳人选(我也是不久前开始的)
好吧,我模拟了我的控制器。
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void addUserTest() throws Exception {
String content = "{" +
"\"username\": \"test\"," +
"\"password\": \"tesT1234\"," +
"\"email\": \"test@test.com\"" +
"}";
mockMvc.perform(
MockMvcRequestBuilders.post("/users/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(content))
.andExpect(status().isCreated());
}
例如,对于注册用户,我构建了一个 JSON 内容并将其发送到我的模拟。我检查最终状态以查看它是否创建了用户。
Do I need to mock something?
这取决于您究竟需要测试什么。
如果您编写单元测试 - 那么您通常会像您假设的那样模拟外部依赖项,它们将遵循一些合同。
Can I just test it like a plain Java method (call it with whatever parameters and assert the output)?
当然可以,但这意味着您正在测试一个 bean 方法,而不是从接收 http 请求到返回响应的完整流程。
如果您需要进行端到端测试,您需要 运行 您的应用,然后调用您的 HTTP 方法并验证响应。为此,您可以按照其中一个答案中的描述使用 MockMvc。
制作一个使用 JDBC 本身的控制器是个坏主意。
使用 Controller-Service-Repository pattern
.
会好很多
所以你在 Foo 控制器中的代码可以像这样:
@RestController
@RequestMapping("/foo")
public class FooController {
@Autowired
private FooService fooService;
@GetMapping("/{name}")
public ResponseEntity<?> byName(@PathVariable String name) {
final List<Foo> list = fooService.getFooByName(name);
return ResponseEntity.ok(list);
}
}
出于测试目的,最好使用 MockMvc:
// In the Foo controller
@GetMapping("/{name}")
public ResponseEntity<?> byName(@PathVariable String name) {
return ResponseEntity.ok("Hello, " + name);
}
// In the Test class
@SpringBootTest
@AutoConfigureMockMvc
class ControllerTests {
@Autowired
private MockMvc mockMvc;
@Autowired
private FooController fooController;
@Test
public void contextLoads() {
assertThat(fooController, is(notNullValue()));
}
@Test
public void getUser() throws Exception {
MockHttpServletResponse response = mockMvc.perform(MockMvcRequestBuilders.get("/foo/Joe"))
.andExpect(status().isOk())
.andDo(print())
.andReturn().getResponse();
assertThat(response.getContentAsString(), is("Hello, Joe"));
}
}
经过一些研究和发布答案的帮助,我将检索代码移到了服务中:
@Service
public class FooService implements IFooService {
@Autowired
private NamedParameterJdbcTemplate template;
// the only method of IFooService
@Override
public List<FormulaFunction> getAllByName(String name) {
Map<String, String> params = new HashMap<>();
params.put("name", name);
return template.query("SELECT * FROM FOOS WHERE FOO_NM = :name", params, new FooRowMapper());
}
}
在测试控制器时,使用 Mockito 模拟该服务:
@WebMvcTest(FooController.class)
public class FooControllerTest {
@MockBean
private IFooService service;
@Autowired
private FooController controller;
@Autowired
private MockMvc mvc;
@Test
public void canCreate() {
Assertions.assertNotNull(controller);
}
/**
* Test that controller properly returns 404 when no results are found
*/
@Test
public void test404() throws Exception {
doReturn(new ArrayList<>()).when(service).getAllByName("bars");
this.mvc.perform(get("/foo/bars")).andExpect(status().isNotFound());
}
}
我有一个 Spring 控制器,如下所示:
@RestController
@RequestMapping("/foo")
public class FooController {
@Autowired
private NamedParameterJdbcTemplate template;
@GetMapping("/{name}")
public List<Foo> byName(@PathVariable String name) {
Map<String, String> params = new HashMap<>();
params.put("name", name);
List<Foo> result = template.query("SELECT * FROM FOOS WHERE FOO_NM = :name", params, new FooRowMapper());
if (result.size() == 0) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, String.format("foo %s not found", name));
}
return result;
}
}
但是,我不确定如何测试它。我可以做一个基本的“Spring 可以设置”测试:
@SpringBootTest
public class FooControllerTest {
@Autowired
private FooController controller;
@Test
public void canCreate() {
Assertions.assertNotNull(controller);
}
}
但我不确定正确的测试方法是什么,例如 byName
方法。我需要嘲笑什么吗?我可以像普通 Java 方法一样测试它(使用任何参数调用它并断言输出)吗?
您可以像常规 Java 方法一样对其进行测试,或者使用 MockMvc 对相关路径执行获取请求。
如果你把你的方法的主体贴出来就好了(现在很难帮忙)。
我根本不是测试控制器的最佳人选(我也是不久前开始的) 好吧,我模拟了我的控制器。
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void addUserTest() throws Exception {
String content = "{" +
"\"username\": \"test\"," +
"\"password\": \"tesT1234\"," +
"\"email\": \"test@test.com\"" +
"}";
mockMvc.perform(
MockMvcRequestBuilders.post("/users/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(content))
.andExpect(status().isCreated());
}
例如,对于注册用户,我构建了一个 JSON 内容并将其发送到我的模拟。我检查最终状态以查看它是否创建了用户。
Do I need to mock something?
这取决于您究竟需要测试什么。 如果您编写单元测试 - 那么您通常会像您假设的那样模拟外部依赖项,它们将遵循一些合同。
Can I just test it like a plain Java method (call it with whatever parameters and assert the output)?
当然可以,但这意味着您正在测试一个 bean 方法,而不是从接收 http 请求到返回响应的完整流程。
如果您需要进行端到端测试,您需要 运行 您的应用,然后调用您的 HTTP 方法并验证响应。为此,您可以按照其中一个答案中的描述使用 MockMvc。
制作一个使用 JDBC 本身的控制器是个坏主意。
使用 Controller-Service-Repository pattern
.
所以你在 Foo 控制器中的代码可以像这样:
@RestController
@RequestMapping("/foo")
public class FooController {
@Autowired
private FooService fooService;
@GetMapping("/{name}")
public ResponseEntity<?> byName(@PathVariable String name) {
final List<Foo> list = fooService.getFooByName(name);
return ResponseEntity.ok(list);
}
}
出于测试目的,最好使用 MockMvc:
// In the Foo controller
@GetMapping("/{name}")
public ResponseEntity<?> byName(@PathVariable String name) {
return ResponseEntity.ok("Hello, " + name);
}
// In the Test class
@SpringBootTest
@AutoConfigureMockMvc
class ControllerTests {
@Autowired
private MockMvc mockMvc;
@Autowired
private FooController fooController;
@Test
public void contextLoads() {
assertThat(fooController, is(notNullValue()));
}
@Test
public void getUser() throws Exception {
MockHttpServletResponse response = mockMvc.perform(MockMvcRequestBuilders.get("/foo/Joe"))
.andExpect(status().isOk())
.andDo(print())
.andReturn().getResponse();
assertThat(response.getContentAsString(), is("Hello, Joe"));
}
}
经过一些研究和发布答案的帮助,我将检索代码移到了服务中:
@Service
public class FooService implements IFooService {
@Autowired
private NamedParameterJdbcTemplate template;
// the only method of IFooService
@Override
public List<FormulaFunction> getAllByName(String name) {
Map<String, String> params = new HashMap<>();
params.put("name", name);
return template.query("SELECT * FROM FOOS WHERE FOO_NM = :name", params, new FooRowMapper());
}
}
在测试控制器时,使用 Mockito 模拟该服务:
@WebMvcTest(FooController.class)
public class FooControllerTest {
@MockBean
private IFooService service;
@Autowired
private FooController controller;
@Autowired
private MockMvc mvc;
@Test
public void canCreate() {
Assertions.assertNotNull(controller);
}
/**
* Test that controller properly returns 404 when no results are found
*/
@Test
public void test404() throws Exception {
doReturn(new ArrayList<>()).when(service).getAllByName("bars");
this.mvc.perform(get("/foo/bars")).andExpect(status().isNotFound());
}
}