제목을 영어로 쓰면 뭔가 거창해 보이죠....ㅋㅋㅋ


하지만 사실상 별건없습니다.


영어로 쓴 이유는 영어를 잘해서도 아니고, 한글로 쓰기에는 뭔가 양이 많아보여서... 즉, 난 어휘력이 약하다는것;;



대다수의 직장인이 그렇겠지만 특히 개발자라면 일정과 항상 씨름하게 됩니다..


그러므로 우리는 TDD  즉, 테스트 주도 설계가 사실상 불가능하죠 ㅋㅋㅋㅋ


TDD 의 핵심은 테스트코드를 선행 작성하는건데, 

대부분이 먼저 코드를 만들고 그걸 검증하기 위해 테스트 코드를 작성합니다.


근데 이것조차 힘든게 바로 대한민국 IT 업계의 현실 아닐까요? ㅋㅋㅋ



개인적으로 나는 개발자가 꼼꼼해야 된다고 보는편인데, 그 이유는 로컬에서야 뭐 이런저런 실수가 나올 수 있지만


Product 에서 사소한 실수는 치명적인 사고로 이어질 수도 있으므로... 

그렇기에 본인이 작성한 코드에 대해 책임을 져야 한다고 봅니다..



즉, 하고싶은말은 결국.... ㅋㅋ 테스트 코드 작성을 생활화 하자 입니다 ㅎㅎ (안하는 사람들이 워낙많아서 ㅠㅠ 힘듬)



어느순간부터 포스팅을 쓰기전에 사담을 늘어놓는데... 글세요 요새 제 의견과 일치하지 않는 환경에서 일하다 보니 영향을 받은것일지도...



본론으로 들어와서, 현재 프로젝트는 Spring Boot 로 개발하고 있습니다. 버전은 거의 최신버전인 3점대로 하고있고요


테스트 코드도 작성하고 있는데.. 


Spring Boot 에서는 테스트 코드가 정말 작성하고 싶어지게 할 만큼.. 매력적입니다...



늘상 그렇듯 테스트 코드 작성하고 로컬에서 Unit Test 코드를 돌리고... 성공이 떨어지면 


CI 서버에서 빌드를 진행하고.... 배포를 하게되죵~



뭐 항상 하던작업이긴 한데... Spring Boot 로 변경하고 나니... 배포 프로세스를 변경하고 싶어집니다... 


빌드하기전 Unit Test 코드실행 후 성공해야 배포하는 것으로요~


(사실 예전에는 빌드하고 배포한 후에 테스트 코드를 돌렸었는데..... >> 아직 많은 곳에서 이렇게 하시죠?)



근데 문제는 그겁니다... 


로컬에서 테스트를 한다고 하면 설정파일을 로컬로 바꿔놓고 하면되는데 ~ (하드코딩으로 한다거나??)


젠킨스 부터는 코드에 손을 못댑니다.. 더군다나 Product 이라면?? 


테스트 코드는 똑같은데 실행환경만 Profile 에 맞게 변경하고 싶다면??? 


사실상 예전에는 생각해 보지 않았던 작업이라서 처음에는 막막했습니다.


그 이유는.... 사실 이전에도 이런 작업을 한 적이 있지만 Quality 검증이었습니다. 


예를들면, PMD , Coverage 등등등... 실제 서비스 로직과는 별개의 그런...


하지만 Spring 은 실제 소스 검증을 가능하게 해줍니다. 


Spring boot 로 접어들면서 embedded tomcat 이 생겨났고, 그러므로 유닛코드를 동작시키면 톰캣이 동작하는것처럼 보입니다.


그리고 나서 실제 요청주소로 테스트 코드에서 작성한 내용들을 검증하게 되죵~


실제 결과부터 볼까요??


사진은 PC 환경에서 클릭할 경우 크게 볼 수 있습니다.



- 먼저 Jenkins 빌드 Console 화면을 볼까요?? 잘 보시면 Result 부분에 Test 결과가 나타나 있습니다. 


모든 테스트가 실패나 에러없이 전부 통과했네요~~ 




- 두번째로 빌드 요약입니다. 보시면 아시겠지만 빌드는 성공했고 아래 실패가 없습니다 라는 문구로 비추어 볼때 테스트도 성공입니다~

아~ 참고로 새벽 2시 아닙니다... AWS 를 사용하고 있는데 기본 시간 설정이 GMT 이다 보니 + 9시간 해야 합니다. 오전 11시에 한거에요 ㅋㅋ




- 젠킨스는 참 이런점이 좋다고 생각되는데~ 테스트 결과를 좀 더 그럴싸 하게 보여줍니다~


또한, 테스트 결과를 외부로 전송할 수도 있습니다. 


나중에는 협업툴인 Slack 으로 테스트 결과를 어떻게 보내는지도 포스팅 할 예정입니다~~



다시 본론으로 돌아와서~ ㅎㅎ 자 그럼 어떻게 이렇게 할 것인지 한번 생각해 봅시다. 



- 위에서 본 것 처럼 로컬에서 테스트 할 경우는 환경설정을 그때그때마다 변경해 주면 되지만


젠킨스에서는 환경설정을 맘대로 바꿀 수 없으니 외부에서 무언가 값을 받아와야 합니다. 


그런면에서~ Spring Boot 는 아주 좋은 해결책을 제시하고 있죠~ 바로 System Environment Variables 입니다. 


args 에다가 -Dname=value 이런식으로 사용합니다 아래 그림을 보시죠~




요런식으로다가 사용할 수 있다는겁니다... 


근데 이것만 있느냐? 아니죠 ㅎㅎ 바로 Maven 실행환경에서도 값을 넘길 수 있다는겁니다.


바로 요런식으로요~~




위 사진은 jenkins 에서 빌드할 때 사용하는 구문입니다. Goals 에는 작업 명령어를 작성하는데 Options 도 작성할 수 있죠


저는 2가지를 썼네요~~ 하나는 -Dgit.commit 이고.. 또 다른 하나는 -Dprofile 인데요 


사실 지금 포스팅과는 상관없긴 하지만 Dgit.commit 은 살짝만 맛보고 넘어가자면


이런 용도로 씁니다~ 


Jenkins 에서 빌드가 끝난 후 배포를 진행하고~ 최종버전으로 잘 배포 되었는지를 판단하는 값으로 쓰입니다. 


위 처럼 -Dgit.commit=${GIT_COMMIT} 이라고 쓰고 빌드&배포를 하게되면 META_INF 에 다음과 같이 나타나게 됩니다. 




요놈을 모니터링 또는 관리자용 사이트에서 Json 형태로 호출해오기만 하면 되는거죠~ 요렇게요





자세한 내용은 이전 포스팅에서 확인해보세요 >> 요기



자 그러면 핵심인 -Dprofile=local 부분을 보겠습니다~ 해당 부분은 maven option 으로 써 POM 파일쪽에 뭔가 값으로 넘길 수 있습니다. 


이 값을 받으려면 Plugin 이 필요하게되는데~ 저는 Maven Surefire Plugin 요걸 사용하기로 했습니다~ 

Maven Surefire Plugin 은 Test 를 위해 만들어진 Plugin 이라고 해도 과언이 아닙니다.. 원하는대로 테스트를 할 수도 있고 


테스트를 건너뛸 수도 있으며, 테스트 결과를 여러 형태로 받아볼 수도 있습니다. 이 외에도 여러가지 Test 관련 기능들이 있죠~ 


직접 들어가셔서 확인해 보세요~~ 요기 >> Maven Surefire Plugin


해당 Plugin 이 제공하는 기능이 여러가지 있지만 그 중에 저희가 사용할 것은 바로 Using System Properties  입니다~


해당 기능을 사용하면 위에서 설명했던 외부 변수를 POM 파일로 받을 수 있습니다. 


그 중에서도 2번째로 설명했던 Maven 빌드 시 Option 을 받아오는걸 사용해 보도록 하겠습니다~


저는 POM 파일에 이렇게 세팅을 해봤습니다~ (정석은 오른쪽입니다~)







profile 이라는 이름으로 ${profile} 를 갖고올 수 있게 해놨네요~ 


그러고 보니 위에서 본 기억이 나지 않으시나요?? 


다시 사진을 볼까요?? 



네 맞습니다 -Dprofile=local 부분이죠~ 이 부분으로 저 값이 세팅됩니다. 


자 그렇다면!!!! 저 값을 어떻게 갖고올것이며, 저 값을 어떻게 Spring Test 에 적용할 것이냐?? 이 부분을 보겠습니다~


먼저 살펴봐야 할 문서가 있는데요 ㅎㅎ 귀찮지만 꼭 봐야됩니다.


바로 Spring Boot Test 관련 문서입니다. 링크 >> ActiveProfilesResolver


Description 부분만 살짝 옮겨왔습니다~ 해석하자면 ... 음... 네 바로 그겁니다...


To resolve active bean definition profiles programmatically, simply implement a custom ActiveProfilesResolver and register it via the resolver attribute of@ActiveProfiles. The following example demonstrates how to implement and register a custom OperatingSystemActiveProfilesResolver. For further information, refer to the corresponding javadocs. 


저는 이렇게 적용했습니다. 


먼저 세팅하는 부분입니다. 

package com.rest.base; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.context.ActiveProfilesResolver; public class DnbbTestActiveProfilesResolver implements ActiveProfilesResolver { private static final Logger logger = LoggerFactory.getLogger(DnbbTestActiveProfilesResolver.class); @Override public String[] resolve(Class<?> testClass) { String profile = System.getProperty("profile"); if (profile == null) { profile = "local"; logger.warn("Default spring profiles active is null. use {} profile.", profile); } return new String[] {profile}; } }




다음으로 실제 적용하는 부분입니다~

package com.rest.dnbb;

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.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.rest.DnbbApplication;
import com.rest.base.DnbbTestActiveProfilesResolver;
import com.rest.base.DnbbTestContextInitializer;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = DnbbApplication.class, initializers = DnbbTestContextInitializer.class)
@WebAppConfiguration
@ActiveProfiles(inheritProfiles = false, resolver = DnbbTestActiveProfilesResolver.class)
public class DnbbUserTest {
	
	@Autowired
	private WebApplicationContext webApplicationContext;
	
	private MockMvc mockMvc;
	
	@Before
	public void before(){
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
	}
	
	@Test
	public void testUserDuplicateEmail() throws Exception {
		mockMvc.perform(
				get("/user/duplicate/email")				
				.param("email", "test@test.com")
				.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
					.andDo(print())
					.andExpect(status().isOk())
					.andExpect(jsonPath("$.message").value("success"));
	}
}


특별히 중요히 보셔야 될 부분은들 하이라이트 처리를 해놨습니다.


위 분홍색으로 하이라이트 처리한 부분이 실제 적용하는 부분입니다~



사실 실제로 작업하는 양도 적고... 뭐랄까 그리 어려운 내용도 아니지만 어떻게 해야될지 모르고.... 어떤 Plugin 및 Spring 이 지원하는지 몰라서 못하시는 분들을 위해


최대한 자세히 쓰려고 했습니다~ 



이번기회에 테스트 코드를 작성해보시고~ 테스트 프로세스를 변경해 보시는건 어떠세요?? 


어쩌면 dev, stage, product 에서 발생 할 중대한 에러를 발견해 내고 뿌듯해 하실지도 모릅니다!! 당장 시작 하시죠 ^^


다음 포스팅 주제는... 역시 테스트와 관련되어 있는데요~ 


MockMvc & Transaction Testing 입니다.




+ Recent posts