며칠전에 작성하다 말았던 테스트 코드를 다시 작성해보았는데, 생각이 안나서 (?) 계속 예제를 참고하며 코딩했다. 
이번에 새로 깨달은 점은 @MockBean을 하는 경우이다. 임시 객체만 생성하고 원하는 값을 주입하고자 할 때 사용하는 것이구나 싶다. 그리고 테스트 케이스도 정석으로 잘되는 동작, 정석이 아닌 경우에도 잘 되는 동작, 에러나는 동작 등 코드의 전체 브랜치를 확인할 수 있게 작성해야 하는 것도 배웠다. 내일은 다른 Controller에 대해 훝어보고, 서비스 코드에서의 테스트 코드는 어떻게 짜는 것인지 공부해보도록 하자. 

package org.springframework.samples.petclinic.owner;

import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.test.web.servlet.MockMvc;

import java.time.LocalDate;


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

@WebMvcTest(OwnerController.class)
public class OwnerControllerTests2 {
	@Autowired
	MockMvc mockMvc;

	@MockBean
	OwnerRepository owners;

	private static final int TEST_OWNER_ID = 1;

	private Owner george() {
		Owner george = new Owner();
		george.setId(TEST_OWNER_ID);
		george.setFirstName("George");
		george.setLastName("Franklin");
		george.setAddress("110 W. Liberty St.");
		george.setCity("Madison");
		george.setTelephone("6085551023");
		Pet max = new Pet();
		PetType dog = new PetType();
		dog.setName("dog");
		max.setType(dog);
		max.setName("Max");
		max.setBirthDate(LocalDate.now());
		george.addPet(max);
		max.setId(1);
		return george;
	};

	@BeforeEach
	void setup() {
		given(this.owners.findByLastName(eq("George"), any(Pageable.class)))
			.willReturn(new PageImpl<Owner>(Lists.newArrayList(george())));

		given(this.owners.findByLastName(eq("Franklin"), any(Pageable.class)))
			.willReturn(new PageImpl<Owner>(Lists.newArrayList(george())));

		given(owners.findByFullName(any(), any(), any(Pageable.class))).willReturn(
			new PageImpl<Owner>(Lists.newArrayList(george()))
		);
		given(owners.findById(TEST_OWNER_ID)).willReturn(
			george()
		);
	}

	@Test
	void testGetOwnerSuccess() throws Exception {
		mockMvc.perform(
			get("/owners")
		).andExpect(status().is3xxRedirection())
			.andExpect(view().name("redirect:/owners/1"));
	}

	@Test
	void testGetOwnersSuccess() throws Exception {
		Mockito.when(this.owners.findByFullName(any(), any(), any(Pageable.class))).thenReturn(
			new PageImpl<Owner>(Lists.newArrayList(george(), george()))
		);

		mockMvc.perform(
			get("/owners")
		).andExpect(status().isOk())
			.andExpect(view().name("owners/ownersList"));
	}
}

스프링 레거시 프로젝트 기반 전시 정보 데이터를 다양한 곳에서 가져와 데이터베이스에 저장하는 프로그램을 작성한 적이 있다. 스프링 부트도 공부할 겸 하여 부트로 전환하면서 새로운 자바 문법 (17)에 맞게 개발을 진행하려고 한다. 
그 전에 서비스의 기능 및 이에 필요한 api 설계 및 앞으로의 계획을 세워보고자 한다.

수집 서비스의 기능
1. 매일 정해진 시간에 자동으로 A, B, C 에서 현재 날짜 기준 전시 중인 정보를 수집해 database에 overwrite하여 저장한다.
2. 전시 정보 수집 도중 실패할 시 이전 데이터로 다시 원복하고, 그 후 1회 정도  (5분 간격) 전시 정보 수집을 재시도 한다.
3. 수집이 끝나면 해당 상태에 대해 slack 또는 email로 수집 완료 알림이 간다. 
(2번과 3번은 1번이 끝나면 시도해봐야겠다.)


필요한 api
먼저 1번의 기능에 필요한 api는 다음과 같다. 그런데 이런 식으로 api를 작성해도 되는 것일까? 
이름의 명칭이라던가 parameter로 오는 것이 하나라 뒤에 붙여주긴 했는데 parameter가 계속 추가되면 뒤에 계속해서 붙여주면 되는 것일까? 싶다. restapi 작성법은 개발하며 다른 프로젝트를 참고하면서 수정을 해 봐야 할 것 같다.

/collectExhibition?service=leeum
/collectExhibition?service=open&type=json



일정
최소 하루 30분을 목표로 일주일 동안 기능 1부터 완성해보려고 한다.

0317 ~ 0323
17, 18 : 프로젝트 셋팅 및 folder 구조 구성 및 api 작성하기 1 (그동안 배웠던 것 : annotation 적용해보기 등)
19 : api 작성하기 2 (병렬적으로 데이터를 긁어와 db에 저장하는 구조 찾아보면서 작성해보기)
20 : api 작성하기 3 
21 : api 작성하기 4 (데이터 베이스는 h2 메모리 데이터 베이스 사용해보기)
22 : 테스트 코드 작성하기 1
23 : 테스트 코드 작성 및 기능 1완료 하기, 2번에 대한 계획 세우기

0324 ~ 0330 : 
2번 기능 진행 ...

0331 ~ 0406: 
3번 기능 진행 ...

'Development > Spring' 카테고리의 다른 글

[Spring-petclinic] 엉망진창 test code 작성하기 - 2  (0) 2024.03.28
JPA 개념 이해  (0) 2023.10.31

"스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 이동욱, 프리렉" 를 따라하던 중 p76 ~ 86에서 JPA란 개념을 처음 접했다. 정리하여 포스팅한다. 

 

1. JPA가 무엇인가?
자바의 JPA라는 표준 기술로써 객체지향적으로 데이터 작성 후 이를 SQL 쿼리로 변환하여 DB에 수행하는 역할을 한다.

2. Hibernate와 Spring Data JPA는 거의 비슷한 기능을 하지만, 후자를 추천하는 데 이유가 있다고 한다.
바로, 구현체와 저장소 교체의 용이성 때문이라고 한다.

 

2.1 구현체 교체의 용이성

예를 들어 Hibernate 대신 다른 구현체를 사용하고 싶다면, 바꾸면 된다. (Spring Data JPA에서 지원한다고 한다.) 

 

2.2 저장소 교체의 용이성
Spring Data 하위의 프로젝트는 동일한 CRUD 인터페이스를 구현하고 있기에, JPA가 아닌 Mongo DB를 사용하고 싶다면 바꾸면 된다. 

 

3. 데이터를 저장하기 위해선, DB 테이블과 매칭될 클래스와 (보통 Entity 클래스라고 한다.) DB Layer 접근자인 Repository 필요하다. 또한 이 둘은 아주 밀접한 관계이기에 (JPA의 경우 JpaRepository<Entity 클래스, PK 타입>으로 상속해 구현함) 함께 위치해야 한다.

+ Recent posts