首先我请求你理解我可怜的工程师。
我正在为Spring Rest Docs库编写make API规范的测试代码。
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.10'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
group = 'com.jogijo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
compileJava.options.encoding = 'UTF-8'
configurations {
asciidoctorExt
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.vladmihalcea:hibernate-types-52:2.17.3'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JPA
implementation 'org.apache.httpcomponents:httpcore:4.4.15'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
//implementation 'org.springframework.boot:spring-boot-starter-log4j2'
implementation 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.2'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// 유효성
implementation 'org.springframework.boot:spring-boot-starter-validation'
// model struct
implementation 'org.mapstruct:mapstruct:1.5.4.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.4.Final'
// security
implementation('org.springframework.boot:spring-boot-starter-security')
// DB
//runtimeOnly ('mysql:mysql-connector-java:8.0.32') //mysql8
runtimeOnly("com.mysql:mysql-connector-j")
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//implementation'mysql:mysql-connector-java'
// mybatis
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.0'
// aws s3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
//jwt
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'
//oauth
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
// Spring Rest Docs
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
}
ext {
snippetsDir = file('build/generated-snippets')
}
tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}
asciidoctor {
inputs.dir snippetsDir
configurations 'asciidoctorExt'
dependsOn test
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
doFirst{
delete file('src/main/resources/static/docs')
}
from file("build/docs/asciidoc")
into file("src/main/resources/static/docs")
}
build {
dependsOn copyDocument
}
bootJar {
dependsOn asciidoctor
from("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
这是build.gradle
@RestController
@RequestMapping("/app/alarms")
@RequiredArgsConstructor
public class AlarmController {
private final AlarmService alarmService;
private final AlarmProvider alarmProvider;
private final JwtService jwtService;
/**
* 알람 1개 불러오기
* @param alarmId
* @return
*/
@GetMapping("/{alarmId}")
public BaseResponse<AlarmRes> GetAlarm(@PathVariable Integer alarmId){
AlarmRes alarmRes = alarmProvider.getAlarm(alarmId);
return new BaseResponse<>(ResponseStatus.SUCCESS, alarmRes);
}
这是我想要测试的API。
package com.wakeUpTogetUp.togetUp.alarms;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wakeUpTogetUp.togetUp.alarms.dto.response.AlarmRes;
import com.wakeUpTogetUp.togetUp.common.ResponseStatus;
import com.wakeUpTogetUp.togetUp.common.dto.BaseResponse;
import com.wakeUpTogetUp.togetUp.utils.JwtService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.mockito.BDDMockito.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(AlarmController.class)
@AutoConfigureRestDocs
class AlarmControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private AlarmService alarmService;
@MockBean
private AlarmProvider alarmProvider;
@MockBean
private JwtService jwtService;
@Test
@DisplayName("getAlarm - [Get] /alarm/{alarmId}")
void getAlarm() throws Exception{
//given
AlarmRes response = AlarmRes.builder()
.id(42)
.userId(9)
.missionId(1)
.name("기상알람")
.icon("⏰")
.sound("default")
.volume(80)
.isVibrate(true)
.isRoutineOn(true)
.snoozeInterval(5)
.snoozeCnt(3)
.startHour(6)
.startMinute(0)
.monday(true)
.tuesday(true)
.wednesday(true)
.thursday(true)
.friday(true)
.saturday(true)
.sunday(false)
.isActivated(true)
.build();
given(alarmProvider.getAlarm(42)).willReturn(response);
Integer alarmId = 42;
//when
ResultActions action = mockMvc.perform(get("/app/alarms/42"))
.andDo(print());
//then
BaseResponse<AlarmRes> responseData = new BaseResponse<>(ResponseStatus.SUCCESS, response);
action.andExpect(status().isOk())
.andExpect(content().json(objectMapper.writeValueAsString(responseData)))
.andExpect(jsonPath("$.result.userId").value(9))
.andDo(
// rest docs 문서 작성 시작
document("alarm/getAlarm", // directory명 위에서 설정한 build/generated-snippets 하위에 생성
// --> build/generated-snippets/member/create
requestParameters( // queryString 관련 변수 정보 입력
parameterWithName("alarmId").description("알람 Id")
),
responseFields( // response data 필드 정보 입력
fieldWithPath("httpStatusCode").description("http 상태코드"),
fieldWithPath("httpReasonPhrase").description("http 상태코드 설명문구"),
fieldWithPath("message").description("설명 메시지"),
subsectionWithPath("result").description("결과"),
fieldWithPath("id").description("알람 Id"),
fieldWithPath("userId").description("사용자 Id")
)
)
);
}
这是测试代码。
MockHttpServletRequest:
HTTP Method = GET
Request URI = /app/alarms/42
Parameters = {}
Headers = []
Body = null
Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest [http://localhost:8080/app/alarms/42]}
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 = 302
Error message = null
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Location:"http://localhost:8080/oauth2/authorization/google"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = [HERE IS REDIRECT URL BUT IT COULD BE SPAM POST. LOCALHOST:8080 / OAUTH2
AUTHORIZATION / GOOGLE]
Cookies = []
MockHttpServletRequest:
HTTP Method = GET
Request URI = /app/alarms/42
Parameters = {}
Headers = []
Body = null
Session Attrs = {SPRING_SECURITY_SAVED_REQUEST=DefaultSavedRequest [http://localhost:8080/app/alarms/42]}
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 = 302
Error message = null
Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Location:"http://localhost:8080/oauth2/authorization/google"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = http://localhost:8080/oauth2/authorization/google
Cookies = []
Status expected:<200> but was:<302>
Expected :200
Actual :302
<Click to see difference>
java.lang.AssertionError: Status expected:<200> but was:<302>
AlarmControllerTest > getAlarm - [Get] /alarm/{alarmId} FAILED
java.lang.AssertionError at AlarmControllerTest.java:79
1 test completed, 1 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/anyti/IdeaProjects/TogetUp/build/reports/tests/test/index.html
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 9s
4 actionable tasks: 2 executed, 2 up-to-date
这是错误代码。
有google oauth登录代码重定向到url。但我不知道为什么这个测试代码会得到这样的响应。它们在不同的控制器中,并且没有回复URL“http://localhost:8080/oauth2/authorization/google”的API
我注解了所有与oauth相关的代码,但它不起作用。我检查了URL,但我认为它是正确的。
我现在真的累坏了。我很高兴如果有人帮助我。
1条答案
按热度按时间6yt4nkrj1#
您的问题中缺少安全配置,但您显然已将应用程序配置为OAuth2客户端(使用Google作为授权服务器),并重定向到登录请求,该请求具有没有授权客户端的会话。
您被重定向到登录,因为您没有为测试请求设置或模拟安全上下文。我建议你读一下我写的Baeldung article。
在您的情况下,可能需要的是:
另一种选择是使用this libs I wrote,并用
@WithOAuth2Login
或@WithOidcLogin
装饰测试方法或类。