728x90
목적
- Spring MVC Web Controllers 테스트
- HTTP requests 생성과 전송
- HTTP response 검증 (status code, view name, model attributes)
Process
- @AutoConfigureMockMvc
- MockMvc 주입
- web requests 수행
- Expectations 정의
- Assert results
테스트 클래스
package com.luv2code.springmvc;
@TestPropertySource("/application.properties")
@AutoConfigureMockMvc
@SpringBootTest
public class GreadeBookControllerTest
{
@Autowired
private JdbcTemplate jdbc;
@Autowired
private MockMvc mockMvc;
@Mock
private StudentAndGradeService studentAndGradeServiceMock;
@Autowired
private StudentDao studentDao;
private static MockHttpServletRequest request;
}
@TestPropertySource("/application.properties") // 설정파일을 설정
@AutoConfigureMockMvc // 컨트롤러뿐만 아니라 @Service, @Repository 객체들도 모두 메모리에 올린다.
JdbcTemplate
@Autowired
private JdbcTemplate jdbc;
직접적으로 sql 실행 가능
MockMvc
@Autowired
private MockMvc mockMvc;
서블릿 컨테이너 사용하지 않고, 스프링 MVC 동작을 재현할 수 있는 클래스
@BeforeAll
private static MockHttpServletRequest request;
@BeforeAll
public static void setup(){ // static 전역으로 정의
request= new MockHttpServletRequest();
request.setParameter("firstname","java");
request.setParameter("lastname","app");
request.setParameter("emailAddress","javapp@tistory.com");
}
테스트 전 uri 파라미터 설정
@BeforeEach
@BeforeEach
public void beforeEach(){
// 학생 생성
jdbc.execute("INSERT INTO student(id, firstname,lastname, email_address)"+
"values(1,'java','app','javapp@tistory)')");
}
테스트 데이터 삽입
@AfterEach
@AfterEach
public void setupAfterTransaction(){
jdbc.execute("DELETE FROM student");
}
테스트 데이터 제거
GET 통신 테스트
@DisplayName("'/' 에 GET 통신으로 어떤 view 네임이 반환되는지")
@Test
public void getStudentHttpRequest() throws Exception{
//given
CollegeStudent studentOne = new GradebookCollegeStudent("java", "app","javapp@tistory.com");
CollegeStudent studentTwo= new GradebookCollegeStudent("java2", "app2","javapp2@tistory.com");
//when
List<CollegeStudent> collegeStudentList = new ArrayList<>(Arrays.asList(studentOne,studentTwo));
// getGradebook의 반환값을 설정
when(studentAndGradeServiceMock.getGradebook()).thenReturn(collegeStudentList);
//then
assertIterableEquals(collegeStudentList, studentAndGradeServiceMock.getGradebook());
// controller 테스트 결과와 view name 테스트
MvcResult mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/")) // path로 접속
.andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
// Assert 테스트
ModelAndViewAssert.assertViewName(mav, "index");
}
URI 접속후 view name이 index 인지 테스트
// controller 테스트 결과와 view name 테스트
MvcResult mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/")) // path로 접속
.andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
// Assert 테스트
ModelAndViewAssert.assertViewName(mav, "index");
Controller
@RequestMapping(value = "/", method = RequestMethod.GET)
public String getStudents(Model m) {
Iterable<CollegeStudent> collegeStudents = studentAndGradeService.getGradebook();
m.addAttribute("students", collegeStudents);
return "index";
}
POST 연결 테스트
@DisplayName("'/' 에 POST 연결, 데이터 전송")
@Test
public void createStudentHttpRequest() throws Exception{
// 대학생 객체 생성
CollegeStudent studentOne = new CollegeStudent("java","app","javapp@tistory.com");
List<CollegeStudent> collegeStudentList = new ArrayList<>(Arrays.asList(studentOne));
// getGradebook() 했을 떄 collegeStudentList가 반환될 때
when(studentAndGradeServiceMock.getGradebook()).thenReturn(collegeStudentList);
// collegeStudentList 반복값과 getGradebook 동작으로 반횐된 값이 같은지
assertIterableEquals(collegeStudentList, studentAndGradeServiceMock.getGradebook());
MvcResult mvcResult= mockMvc.perform(post("/")
.contentType(MediaType.APPLICATION_JSON)
.param("firstname",request.getParameterValues("firstname"))
.param("lastname",request.getParameterValues("lastname"))
.param("emailAddress",request.getParameterValues("emailAddress"))
).andExpect(status().isOk()).andReturn();
ModelAndView mav = mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav,"index");
// controller 요청 했을 때
// database 값 체크
CollegeStudent verifyStudent = studentDao.findByEmailAddress("javapp@tistory.com");
assertNotNull(verifyStudent,"학생 찾음");
}
Controller
@PostMapping(value="/")
public String createStudent(@ModelAttribute("student") CollegeStudent student, Model model)
{
studentAndGradeService.createStudent(student.getFirstname(), student.getLastname(),student.getEmailAddress());
Iterable<CollegeStudent> collegeStudents = studentAndGradeService.getGradebook();
model.addAttribute("students",collegeStudents);
return "index";
}
@DisplayName("학생 row 값 지우기")
@Test
public void deleteStudentHttpRequest() throws Exception{
//1. @BeforeEach 에서 1번 id 에 학생을 삽입한 값 존재 확인
assertTrue(studentDao.findById(1).isPresent());
//2. delete uri 테스트
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders
.get("/delete/student/{id}", 1))
.andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav, "index");
assertFalse(studentDao.findById(1).isPresent()); //삭제됐음 성공
}
@DisplayName("존재하지않은 학생 삭제할때 에러 페이지")
@Test
public void deleteStudentHttpRequestErrorPage() throws Exception{
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders
.get("/delete/student/{id}", 0))
.andExpect(status().isOk()).andReturn();
ModelAndView mav = mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav,"error");
}
전체코드
@TestPropertySource("/application-test.properties")
@AutoConfigureMockMvc
@SpringBootTest
public class GreadeBookControllerTest
{
@Autowired
private JdbcTemplate jdbc;
@Autowired
private MockMvc mockMvc;
@Mock
private StudentAndGradeService studentAndGradeServiceMock;
@Autowired
private StudentDao studentDao;
@Autowired
private MathGradesDao mathGradesDao;
@Autowired
private StudentAndGradeService studentAndGradeService;
private static MockHttpServletRequest request;
@Value("${spl.script.create.student}")
private String sqlAddstudent;
@Value("${sql.script.create.math.grade}")
private String sqlAddMathGrade;
@Value("${spl.script.create.science.grade}")
private String sqlAddScienceGrade;
@Value("${spl.script.create.history.grade}")
private String sqlAddHistoryGrade;
@Value("${sql.script.delete.stduent}")
private String sqlDeleteStudent;
@Value("${sql.script.delete.math.grade}")
private String sqlDeleteMathGrade;
@Value("${sql.script.delete.science.grade}")
private String sqlDeleteScienceGrade;
@Value("${sql.script.delete.history.grade}")
private String sqlDeleteHistoryGrade;
@BeforeAll
public static void setup(){ // static 전역으로 정의
request= new MockHttpServletRequest();
request.setParameter("firstname","java2");
request.setParameter("lastname","app2");
request.setParameter("emailAddress","javapp2@tistory.com");
}
@BeforeEach
public void beforeEach(){
// 학생 생성
jdbc.execute(sqlAddstudent);
jdbc.execute(sqlAddMathGrade);
jdbc.execute(sqlAddScienceGrade);
jdbc.execute(sqlAddHistoryGrade);
}
@DisplayName("'/' 에 GET 통신으로 어떤 view 네임이 반환되는지")
@Test
public void getStudentHttpRequest() throws Exception{
//given
CollegeStudent studentOne = new GradebookCollegeStudent("java", "app","javapp@tistory.com");
CollegeStudent studentTwo= new GradebookCollegeStudent("java2", "app2","javapp2@tistory.com");
//when
List<CollegeStudent> collegeStudentList = new ArrayList<>(Arrays.asList(studentOne,studentTwo));
// getGradebook의 반환값을 설정
when(studentAndGradeServiceMock.getGradebook()).thenReturn(collegeStudentList);
//then
assertIterableEquals(collegeStudentList, studentAndGradeServiceMock.getGradebook());
// controller 테스트 결과와 view name 테스트
MvcResult mvcResult= mockMvc.perform(MockMvcRequestBuilders.get("/")) // path로 접속
.andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
// Assert 테스트
ModelAndViewAssert.assertViewName(mav, "index");
}
@DisplayName("'/' 에 POST 연결, 데이터 전송")
@Test
public void createStudentHttpRequest() throws Exception{
// 대학생 객체 생성
CollegeStudent studentOne = new CollegeStudent("java2","app2","javapp2@tistory.com");
List<CollegeStudent> collegeStudentList = new ArrayList<>(Arrays.asList(studentOne));
// getGradebook() 했을 떄 collegeStudentList가 반환될 때
when(studentAndGradeServiceMock.getGradebook()).thenReturn(collegeStudentList);
// collegeStudentList 반복값과 getGradebook 동작으로 반횐된 값이 같은지
assertIterableEquals(collegeStudentList, studentAndGradeServiceMock.getGradebook());
MvcResult mvcResult= mockMvc.perform(post("/")
.contentType(MediaType.APPLICATION_JSON)
.param("firstname",request.getParameterValues("firstname"))
.param("lastname",request.getParameterValues("lastname"))
.param("emailAddress",request.getParameterValues("emailAddress"))
).andExpect(status().isOk()).andReturn();
ModelAndView mav = mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav,"index");
// controller 요청 했을 때
// database 값 체크
CollegeStudent verifyStudent = studentDao.findByEmailAddress("javapp@tistory.com");
assertNotNull(verifyStudent,"학생 찾음");
}
@DisplayName("학생 row 값 지우기")
@Test
public void deleteStudentHttpRequest() throws Exception{
//1. @BeforeEach 에서 1번 id 에 학생을 삽입한 값 존재 확인
assertTrue(studentDao.findById(1).isPresent());
//2. delete uri 테스트
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders
.get("/delete/student/{id}", 1))
.andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav, "index");
assertFalse(studentDao.findById(1).isPresent()); //삭제됐음 성공
}
@DisplayName("존재하지않은 학생 삭제할때 에러 페이지")
@Test
public void deleteStudentHttpRequestErrorPage() throws Exception{
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders
.get("/delete/student/{id}", 0))
.andExpect(status().isOk()).andReturn();
ModelAndView mav = mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav,"error");
}
/* 새로운 내용 시작 */
@Test
public void 존재하않는학생정보요청_HttpRequest() throws Exception{
assertFalse(studentDao.findById(0).isPresent());
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/studentInformation/{id}", 0))
.andExpect(status().isOk()).andReturn();
ModelAndView mav = mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav,"error");
}
@Test
public void 유효한학생정보요청() throws Exception{
assertTrue(studentDao.findById(1).isPresent());
GradebookCollegeStudent student = studentAndGradeService.studentInformation(1);
assertEquals( 1, student.getStudentGrades().getMathGradeResults().size());
MvcResult mvcResult = this.mockMvc.perform(post("/grades")
.contentType(MediaType.APPLICATION_JSON)
.param("grade", "85.00")
.param("gradeType","math")
.param("studentId", "1")
).andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav, "studentInformation");
// 학생이 잘 생성되었는지 등록확인
student = studentAndGradeService.studentInformation(1);
assertEquals(2, student.getStudentGrades().getMathGradeResults().size());
}
@Test
public void 학생정보가_없을떄_성적생성() throws Exception
{
MvcResult mvcResult = this.mockMvc.perform(post("/grades")
.contentType(MediaType.APPLICATION_JSON)
.param("grade", "85.00")
.param("gradeType","math")
.param("studentId", "0") // 0 : 존재하지 않는 학생 아이디
).andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav, "error");
}
@Test
public void 등급유형이나_제목오류() throws Exception
{
MvcResult mvcResult = this.mockMvc.perform(post("/grades")
.contentType(MediaType.APPLICATION_JSON)
.param("grade", "85.00")
.param("gradeType","literature") // 없는 과목
.param("studentId", "1") //
).andExpect(status().isOk()).andReturn();
ModelAndView mav= mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav, "error");
}
@Test
public void 성적삭제() throws Exception
{
// 실제 성적 검색 확인
Optional<MathGrade> mathGrade = mathGradesDao.findById(1);
assertTrue(mathGrade.isPresent());
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/grades/{id}/{gradeType}",1,"math"))
.andExpect(status().isOk()).andReturn();
ModelAndView mav = mvcResult.getModelAndView();
ModelAndViewAssert.assertViewName(mav, "studentInformation");
mathGrade = mathGradesDao.findById(1);
assertFalse(mathGrade.isPresent());
}
@AfterEach
public void setupAfterTransaction(){
jdbc.execute(sqlDeleteStudent);
jdbc.execute(sqlDeleteMathGrade);
jdbc.execute(sqlDeleteScienceGrade);
jdbc.execute(sqlDeleteHistoryGrade);
}
}
org.opentest4j.AssertionFailedError: 학생 찾음 ==> expected: not <null>
오타때문에 실패할 수 있다.
'Back-end > 테스트' 카테고리의 다른 글
Spring Boot Rest APIs Test (0) | 2022.11.14 |
---|---|
Spring Boot MVC 서비스 테스트 (0) | 2022.11.09 |
Spring Boot MVC 데이터베이스 통합 테스트 @Sql (0) | 2022.10.29 |
Spring Boot Unit Testing - Mocking with Mockito - @MockBean, ReflectionTestUtils (0) | 2022.10.21 |
Spring Boot Unit Testing Support - 1 (0) | 2022.10.10 |
댓글