PostController
테스트 할거야! //test일때 실행한다는 것
//즉 scope가 test일 때를 의미
testImplementation ('org.junit.vintage:junit-vintage-engine'){
//junit 빈티지에서 중복되는 아이 있어서 아래 애는 제외할겡 해주는 것
exclude group : org.hamcrest, module:'hamcrest-core'
}
BoardRepository
지우고
BoardEntity
지우려고 하면 에러날 것
=> 이때 safeEdit 으로 지우기 시도하면 지금 지우려는 애가 쓰이고 있는 부분들 모두 보여준다 , 이 부분 잘 처리 후 safeDelete 수행하면 잘 지워지게 됨
config
패키지에 JpaAuditConfig
클래스 추가
@Configuration
@EnableJpaAuditing
//테스트하면서 jpa를 활용할텐데 원래는 이 어노테이션 JpaApplication클래스에 있삼
//근데 그렇게 되면 어플리케이션 전체 굴림, 근데 jpa를 다 체크하면서 짜기엔 너무 과함
//우린 몇개 어플리케이션만 굴릴거라서 여기다가 붙여줄 거임
//그래서 거기서 삭제 후 여기서 jpa 를 활용할 것임
public class JpaAuditConfig {
}
PostController
PostControllerTest
@RunWith(SpringRunner.class)
@WebMvcTest(PostController.class)
//Controller, ControllerAdvice 만 ㄱㄴ
//테스트 사용할 때 지정된 애만 테스트하기위한 어노테이션
//컨트롤러 요청에 들어오는 애들 테스트 하기위함이다 명시
class PostControllerTest {
//서비스같은애들은 따로 빈객체로 안만들어져서
// 이렇게 서비스따로 자동와이아해줘도 빈안만드러져
// @Autowired
// private PostService postService;
//그래서 서비스인"척"해줄 아이를 mock으로 만들거야
@Autowired
private MockMvc mockMvc;
//HTTP클라이언트인척하는애
//perform이라는 메소드로 http에게 해당
//url요청을 보낸 것 같이 행동
@MockBean
//실제로 만들어지지 않은 빈을 임의로 만든 것
//
private PostService postService;
@Test
void readPost() throws Exception{//perfoem이 예외 던질 것 대비해서
//3단계로 테스트 케이스
//1단계 : given : 어떤 데이터가 준비 돼있다
//PostEntity가 존재할 때(PostService가 PostEntity를 잘 돌려줄 때)
final int id = 10;
PostDto testDto = new PostDto();
testDto.setId(10);
testDto.setTitle("Unit Title");
testDto.setContent("Unit Content");
testDto.setWriter("unit");
//위의 아이는 아무기능도 가지지 않음 - 역할 부여 해야함
//mockito.BDDMockito 에서 기능 부여해줄 given 사용
//postService가 이 메소드를 수행하면 일어날 일을 정의해주는 것
given(postService.readPost(id)).willReturn(testDto);
//2단계 : when : 어떠한 행위가 일어났을 때(함수호출 등)
//경로에 GET 요청이 온다면
final ResultActions actions = mockMvc.perform(get("/post/10"))
.andDo(print());
//actions라는 아이에게는 저러한 결과가 담기게 된다
//3단계 : then : 어떤 결과가 올 것인지
//PostDto가 반환된다
actions.andExpectAll(
status().isOk(),//status가 200인지
content().contentType(MediaType.APPLICATION_JSON),
//돌아온 값이 json인지 확인 & 아래는 json을 위한 정규표현식
//$는 제이슨 한 문서를 의미 - 그 안에 title의 키 값이 is에 들가는 건지
jsonPath("$.title", is("Unit Title")),
jsonPath("$.content",is("Unit Content")),
jsonPath("$.writer", is("unit"))
);
//actions에 정의된 함수들이 andExpectAll을 통해 출력됨
}
@Test
void readPostAll() {
}
}
PostControllerTest
에서 오른쪽 마우스 누르고 run test 하고 콘솔을 확인해본다면 @Test
void readPostAll() throws Exception{
//given : 어떤 데이터가 주어질 것이다
//given으로 할 때 given(내가테스트할함수).willreturn(내가 지정한 데이터)
//까지 지정해줘야 when으로 넘어가기
PostDto post1 = new PostDto();
post1.setTitle("title1");
post1.setContent("test1");
post1.setWriter("testw1");
PostDto post2 = new PostDto();
post2.setTitle("title2");
post2.setContent("test2");
post2.setWriter("testw2");
List<PostDto> readAllPost = Arrays.asList(post1,post2);
given(postService.readPostAll()).willReturn(readAllPost);
//when
final ResultActions actions = mockMvc.perform(get("/post"))
.andDo(print());
//then
actions.andExpectAll(
status().isOk(),
content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON),
jsonPath("$",hasSize(readAllPost.size()))
);
}
WebMVC/DataJpa 는 일부분만 체크
SpringBootTest는 스프링 어플리케이션 전체 테스트
@RunWith(SpringRunner.class) @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )//얘는 모든 빈을 테스트하기 위한 어노테이션
@AutoConfigureMockMvc
//웹 애플리케이션에서 컨트롤러를 테스트할 때, 서블릿 컨테이너를 모킹하기 위해서
// @WebMvcTest를 사용하거나 @AutoConfigureMockMvc를 사용
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
@AutoConfigureTestDatabase
public class PostControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private PostRepository postRepository;
@Before
//테스트 진행하면서 임의의 엔티티들 많이 만들어줘야 해서
//아예 함수로 빼준 상황
public void setEntities(){
createTestPost("first post", "first post content", "first writer");
createTestPost("second post", "second post content", "second writer");
}
//read 테스트를 할 때 아이디로 읽어오기 위해서
@Test
public void givenVadlidId_whenReadPost_then200() throws Exception{
//given
Long id = createTestPost("Read Post", "Created on readPost()", "readPost" );
//when
final ResultActions actions = mockMvc.perform(get("/post/{id}",id))
.andDo(print());
//then
actions
.andExpectAll(
status().isOk(),
content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON),
jsonPath("$.title", is("Read Post")),
jsonPath("$.content", is("Created on readPost()")),
jsonPath("$.writer", is("readPost"))
);
}
//테스트용 엔티티 만들어주는 함수
private Long createTestPost(String title , String content, String writer){
PostEntity postEntity = new PostEntity();
postEntity.setTitle(title);
postEntity.setContent(content);
postEntity.setWriter(writer);
return postRepository.save(postEntity).getId();
}
}
- 에러가 나는데 아마도 sql 관련 에러인 듯 싶은데 원인을 찾을 수 없다. 나중에 찾도록 할 것
## Test Driven Development란? (심화)
- 테스트 주도 개발
: 실제 작동하는 코드 이전에 통과해야 할 테스트를 우선 만드는 개발 방식
- 코드를 작성하기 전에 미리 테스트를 작성하고서, 이에 맞는 코드를 작성해나가는 것!
(ex) 우리가 아까 삭제한 board관련 아이를 만들기 위해서 미리 test 를 제작해놓고, 이후에 코드를 테스트에 맞춰서 작성해나가는 것
![](https://velog.velcdn.com/images%2Fmyway00%2Fpost%2F0637bd06-8621-4b92-ae76-2b2843fef36f%2Fimage.png)
Red > Green > Refactor
## tdd 를 적용해 Board 제작
### (1) BoardControllerTest :
![](https://velog.velcdn.com/images%2Fmyway00%2Fpost%2Fd2cf1447-4c24-4385-b5ed-5f389fdf12f1%2Fimage.png)
- 없는 보드 컨트롤러를 만들었으니 당연히 빨간줄
- 타겟 데스티네이션을 main으로 해서 생성
![](https://velog.velcdn.com/images%2Fmyway00%2Fpost%2F8c72dc42-5368-4360-aa85-49d2c852c195%2Fimage.png)
`BoardControllerTest`
```java
@RunWith(SpringRunner.class)
@WebMvcTest(BoardController.class)
//없는 보드 컨트롤러를 만들었으니 당연히 빨간줄
//타겟 데스티네이션을 main으로 해서 생성
public class BoardControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void createBoard() throws Exception {
//(1) given
BoardDto boardDto = new BoardDto();
//얘도 똑같이 만들고
boardDto.setName("notice");
//setname 해봤는데 boarddto에 없어 그럼 또 추가
//(2) when
final ResultActions actions = mockMvc.perform(post("/board"))
.andDo(print());
//(3) then
actions.andExpectAll(
status().is2xxSuccessful()
);
}
BoardController
@RequestMapping("board")
@RestController
public class BoardController {
}
여전히 클래스에 대해서만 우리가 requestmapping 붙여줌
상세한 메소드들도 정의 & mapping 해줘야 해
일단은 testcode 수정
@RunWith(SpringRunner.class)
@WebMvcTest(BoardController.class)
//없는 보드 컨트롤러를 만들었으니 당연히 빨간줄
//타겟 데스티네이션을 main으로 해서 생성
public class BoardControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void createBoard() throws Exception {
//(1) given
BoardDto boardDto = new BoardDto();
//얘도 똑같이 만들고
boardDto.setName("notice");
//setname 해봤는데 boarddto에 없어 그럼 또 추가
//(2) when
//여기에 지금 request 요청 임의 만드는 거니깐 requestBody도 추가
final ResultActions actions = mockMvc.perform(post("/board")
.contentType(MediaType.APPLICATION_JSON)
//.contentType(boardDto)//boardDto는 스트링 타입이어야지
.content(toJson(boardDto))//json의 형태로
)
.andDo(print());
//(3) then
actions.andExpectAll(
status().is2xxSuccessful(),
content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON),
jsonPath("%.name", is("notice"))
);
}
//자바 객체를 byte 로 돌려주는 아이 - Gson으로도 가능
private byte[] toJson(Object object) throws IOException{
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.writeValueAsBytes(object);
}
}
이제 만들면서 Dto도 만들어주고,,
public class BoardDto {
private String name;
컨트롤러도 마저 짜주고
@RequestMapping("board")
@RestController
public class BoardController {
@PostMapping
public BoardDto createBoard(@RequestBody BoardDto dto){
return new BoardDto(dto.getName());
}
}
테스트 코드도 수정하고
Service 용 MockBean까지 붙여주자
//서비스 추가 근데 서비스 없어서 제작해주기
@MockBean
private BoardService boardService;
그리고 위와 같이 동일하게 서비스 만들어주고 그 안에 dto 를 가지고 수행하고 등등..
코드 익숙해지면 tdd 방식으로 구현 진행진행
PostControllerIntegrationTest
테스트를 수행하다가 발생
Error executing DDL
enerationTarget encountered exception accepting command : Error executing DDL "create table
n is org.hibernate.exception.SQLGrammarException: could not prepare statement
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [insert into
post
(created_at
,updated_at
,content
,title
,writer
) values (?, ?, ?, ?, ?)]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
- 뭔가 sql 구문 오류 같음 찾을 수 없어