스프링 시큐리티를 적용하고 나서 TodoController를 이렇게 작성해주었다.
@WebMvcTest(TodoController.class)
class TodoControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TodoService todoService;
@MockBean
private JwtUtil jwtUtil;
@Test
void todo_단건_조회에_성공한다() throws Exception {
// given
long todoId = 1L;
String title = "title";
CustomUserDetails authUser = new CustomUserDetails(1L, "email", "test1234!", UserRole.USER, "nickname");
User user = User.fromAuthUser(authUser);
UserResponse userResponse = new UserResponse(user.getId(), user.getEmail());
TodoResponse response = new TodoResponse(
todoId,
title,
"contents",
"Sunny",
userResponse,
LocalDateTime.now(),
LocalDateTime.now()
);
// 실제 JWT 토큰 생성
String token = jwtUtil.createToken(authUser.getId(), authUser.getEmail(), authUser.getUserRole(), authUser.getNickname());
// when
when(todoService.getTodo(todoId)).thenReturn(response);
// then
mockMvc.perform(get("/todos/{todoId}", todoId)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(todoId))
.andExpect(jsonPath("$.title").value(title));
}
@Test
void todo_단건_조회_시_todo가_존재하지_않아_예외가_발생한다() throws Exception {
// given
long todoId = 1L;
CustomUserDetails authUser = new CustomUserDetails(1L, "test@email.com", "test1234!", UserRole.USER, "test");
String token = jwtUtil.createToken(authUser.getId(), authUser.getEmail(), authUser.getUserRole(), authUser.getNickname());
// when
when(todoService.getTodo(todoId))
.thenThrow(new InvalidRequestException("Todo not found"));
// then
mockMvc.perform(get("/todos/{todoId}", todoId)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.name()))
.andExpect(jsonPath("$.code").value(HttpStatus.BAD_REQUEST.value()))
.andExpect(jsonPath("$.message").value("Todo not found"));
}
}
실제 토큰을 생성해서 인증하는 방식을 사용하려고 했는데,
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
02:50:00.053 [Test worker] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [org.example.expert.domain.todo.controller.TodoControllerTest]: TodoControllerTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
02:50:00.293 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration org.example.expert.ExpertApplication for test class org.example.expert.domain.todo.controller.TodoControllerTest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.3)
2024-10-06T02:50:00.977+09:00 INFO 18892 --- [ Test worker] o.e.e.d.t.controller.TodoControllerTest : Starting TodoControllerTest using Java 17.0.12 with PID 18892 (started by kikye in C:\Users\kikye\Desktop\Spring\spring-plus)
2024-10-06T02:50:00.979+09:00 INFO 18892 --- [ Test worker] o.e.e.d.t.controller.TodoControllerTest : No active profile set, falling back to 1 default profile: "default"
2024-10-06T02:50:03.952+09:00 WARN 18892 --- [ Test worker] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: cb0bc4f3-7e7f-4255-a447-285cdfb43777
This generated password is for development use only. Your security configuration must be updated before running your application in production.
2024-10-06T02:50:04.523+09:00 WARN 18892 --- [ Test worker] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates, check your Thymeleaf configuration, or set spring.thymeleaf.check-template-location=false)
2024-10-06T02:50:04.596+09:00 INFO 18892 --- [ Test worker] r$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with UserDetailsService bean with name inMemoryUserDetailsManager
2024-10-06T02:50:04.788+09:00 INFO 18892 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2024-10-06T02:50:04.788+09:00 INFO 18892 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
2024-10-06T02:50:04.790+09:00 INFO 18892 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 2 ms
2024-10-06T02:50:04.830+09:00 INFO 18892 --- [ Test worker] o.e.e.d.t.controller.TodoControllerTest : Started TodoControllerTest in 4.385 seconds (process running for 6.255)
MockHttpServletRequest:
HTTP Method = GET
Request URI = /todos/1
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Authorization:"Bearer null"]
Body = null
Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest [http://localhost/todos/1?continue]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 401
Error message = Unauthorized
Headers = [WWW-Authenticate:"Basic realm="Realm"", X-Content-Type-Options:"nosniff", X-XSS-Protection:"0", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Status
Expected :200
Actual :401
<Click to see difference>
java.lang.AssertionError: Status expected:<200> but was:<401>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:637)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214)
at org.example.expert.domain.todo.controller.TodoControllerTest.todo_단건_조회에_성공한다(TodoControllerTest.java:68)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
MockHttpServletRequest:
HTTP Method = GET
Request URI = /todos/1
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Authorization:"Bearer null"]
Body = null
Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest [http://localhost/todos/1?continue]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 401
Error message = Unauthorized
Headers = [WWW-Authenticate:"Basic realm="Realm"", X-Content-Type-Options:"nosniff", X-XSS-Protection:"0", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Status
Expected :400
Actual :401
<Click to see difference>
java.lang.AssertionError: Status expected:<400> but was:<401>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:637)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:214)
at org.example.expert.domain.todo.controller.TodoControllerTest.todo_단건_조회_시_todo가_존재하지_않아_예외가_발생한다(TodoControllerTest.java:89)
at java.base/java.lang.reflect.Method.invoke(Method.java:569)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
> Task :test
TodoControllerTest > todo_단건_조회에_성공한다() FAILED
java.lang.AssertionError at TodoControllerTest.java:68
TodoControllerTest > todo_단건_조회_시_todo가_존재하지_않아_예외가_발생한다() FAILED
java.lang.AssertionError at TodoControllerTest.java:89
2 tests completed, 2 failed
> Task :test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///C:/Users/kikye/Desktop/Spring/spring-plus/build/reports/tests/test/index.html
* Try:
> Run with --scan to get full insights.
BUILD FAILED in 8s
4 actionable tasks: 1 executed, 3 up-to-date
이러한 에러가 발생했다.
주요 내용을 보니 Authorization:"Bearer null"이 되어 토큰이 발생하지 않는다는 것이 원인이었다는 것을 알았다.
[Junit] 유닛 테스트에서 응답코드가 401로 뜨는 경우 해결방법
크롬을 켜서 실제 테스트로 진행하면 200 http response code가 뜨지만 유닛 테스트를 돌려서 테스트를 진행하면 401 오류가 발생하는 경우가 있다. java.lang.AssertionError: Status expected: but was: 필요:200 실제
devjh.tistory.com
[Spring]Security적용 후 Controller 테스트코드 작성 시 발생했던 오류들 (Feat. Junit5, csrf)
Controller에서 모든 Board를 조회하는 메소드를 테스트 코드를 작성하던 중 일어난 일이다. 우선 코드를 먼저보자 사실 이 로직상은 아무 문제가 없다.하지만 다음과 같이 401 Unauthorized가 발생하는
velog.io
두 블로그를 정독하여 읽어보니,
내가 쓴 테스트 코드에서는 JwtFilter 부분으로 권한이 넘어가지 않아서 그렇다는 것을 알았다.
그래서 @WithMockUser(rolse = "USER")를 사용해서
USER라는 권한을 추가해서 테스트를 해보았다.
그러니 정상적으로 확인하는 것을 알 수 있었다.
'Spring' 카테고리의 다른 글
[트러블슈팅] localhost:63342 오류 | localhost:63342 웹소켓 연결 안됨 | localhost:63342 연결 안됨 (0) | 2024.11.05 |
---|---|
[Spring Boot] WebSocket 사용법 | 웹소켓 사용법 | 웹소켓 실시간 알림 | 웹소켓 프론트 | 웹소켓 테스트하는 법 | 웹소켓 html (0) | 2024.10.29 |
[Spring] Spring Boot에서 JPA QueryDSL 사용 | QueryDSL 사용법 | QueryDSL 의존성 추가 (2) | 2024.10.03 |
[Spring] 스프링 카카오 소셜 로그인 구현 | 깃 카카오 소셜 로그인 코드 (5) | 2024.09.25 |
[Spring] 컨테이너 및 도커 개념정리 (0) | 2024.09.19 |