#java #reactjs #spring #spring-boot #mockmvc
#Ява #реагирует на #весна #пружинный ботинок #mockmvc
Вопрос:
У меня есть метод в моем пользовательском контроллере моего API SpringBoot REST, который регистрирует пользователя, анализируя объект json в строку, а затем создавая новый объект пользователя и сохраняя его в базе данных с помощью репозитория JPA.
В моем отдельном интерфейсном проекте CORS React, когда пользователь переходит на домашнюю страницу, в API отправляется запрос POST, который успешно регистрирует пользователя (в качестве теста) с полезной нагрузкой JSON:
{username: "mitton", password: "JohnSmith72-"}
Это работает нормально, и когда я захожу в проект React /home, я вижу, что пользователь был успешно создан и сохранен, поэтому я знаю, что эта функция работает.
Я пытаюсь протестировать этот метод контроллера с помощью JUnit и MockMvc, но когда я пытаюсь сделать тот же запрос POST с помощью MockMvc, возвращается 200, но пользователь не создается.
UserControllerTest.java
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class UserControllerTest { @Autowired MockMvc mvc; @MockBean UserService userService; @Test public void addUserTest() throws Exception { TestUser testUser = new TestUser("timcard", "TimCard72-"); mvc.perform(post("http://localhost:8080/users/signup") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .content(asJsonString(testUser))) .andExpect(status().isOk()); Optionallt;Usergt; user = userService.getUser(1L); Optionallt;Usergt; justInCaseUser = userService.getUser(1L); System.out.println(user.toString()); System.out.println(justInCaseUser.toString()); } public static String asJsonString(final Object obj) { try { final ObjectMapper mapper = new ObjectMapper(); final String jsonContent = mapper.writeValueAsString(obj); System.out.println(jsonContent); return jsonContent; } catch (Exception e) { throw new RuntimeException(e); } } }
Ответ:
Запрос / ответ MockMvc / что печатается:
{"username":"timcard","password":"TimCard72-"} Optional.empty Optional.empty
Full successful react request:
componentDidMount() { const payload2 = { "username": "mitton", "password": "JohnSmith72-" } fetch(SIGNUP_URL, { method: 'POST', headers: { "Accept": "application/json", "Content-Type": "application/json" }, body: JSON.stringify(payload2) }) .then(response =gt; response.json()) .then((data) =gt; { console.log(data); }); }
In request browser: General:
Request URL: http://localhost:8080/users/signup Request Method: POST Status Code: 200 Remote Address: [::1]:8080 Referrer Policy: strict-origin-when-cross-origin
Headers:
Accept: application/json Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Connection: keep-alive Content-Length: 47 Content-Type: application/json Host: localhost:8080 Origin: http://localhost:3000 Referer: http://localhost:3000/ sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Microsoft Edge";v="96" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-site User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36 Edg/96.0.1054.41
Payload:
{username: "mitton", password: "JohnSmith72-"}
The reason I created a TestUser class to use as a temporary object class to map the JSON payload in the test as opposed to using the User entity class is because when I create the User entity object, it automatically generates the ID field, and this would cause issues as the controller logic is supposed to only work with a json string as above with a username, password, not also a generated ID.
TestUser:
@Getter @Setter @AllArgsConstructor @NoArgsConstructor public class TestUser { @Column(name = "username", unique = true) private String username; @Column(name = "password") private String password; }
UserController:
@RestController @RequestMapping(value = "/users") @CrossOrigin(origins = {"http://localhost:3000", "http://localhost:8080"}) public class UserController { final UserService userService; public UserController(UserService userService) { this.userService = userService; } @PostMapping("/signup") public void signUp(@RequestBody User user) { userService.signUpUser(user); } }
Обслуживание пользователей:
public void signUpUser(User user) { user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); userRepository.save(user); }
Я чувствую, что проблема с тестируемым пользователем может нарушить ее, но она не работала, когда я просто использовал сущность пользователя, потому что значение для идентификатора создавалось автоматически:
@Entity(name = "User") @Table(name = "user") @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "username", unique = true) private String username; @Column(name = "password") private String password; public User(String username, String password) { this.username = username; this.password = password; } }
Комментарии:
1. И почему это должно быть так? Вы издеваетесь над службой… ПОЭТОМУ, что бы ни делала служба, она этого не делает, поскольку экземпляр заменяется издевательским экземпляром.
2. @M. Deinum О, так есть ли какой-нибудь способ сделать это, не создавая издевательский сервис или что-то в этом роде? Я действительно просто хотел проверить это так, как если бы проект React отправлял запросы в API. Спасибо.
3. Не используйте
@MockBean
просто используйте@Autowired
… Это не более сложно, чем это. Или используйте издевательский экземпляр и просто проверьте, вызывается ли метод сохранения служб, вместо того, чтобы пытаться узнать, сохранен ли пользователь в базе данных.4. @M. Deinum Спасибо за вашу помощь, работает отлично!
5. Ну, вы могли бы их использовать, но, вообще говоря, проводить тесты, зависящие друг от друга, — плохая идея. Если ваш заказ на тестирование изменится или вы добавите новые тесты, все изменится, и вы внезапно столкнетесь с трудностями в отслеживании сбоев тестирования.