[오픈소스컨설팅]Gradle Basic - How to use Gradle in Java ProjectJi-Woong Choi
This document explains how to use gradle in Java project. It contains a real sample build file using Spring MVC project which is using education example in OSC.
[오픈소스컨설팅]Gradle Basic - How to use Gradle in Java ProjectJi-Woong Choi
This document explains how to use gradle in Java project. It contains a real sample build file using Spring MVC project which is using education example in OSC.
Scala, Spring-Boot, JPA를 활용한 웹 애플리케이션 개발 과정에 대해 다룬다. Spring-Boot와 JPA 조합만으로도 생산성 있는 웹 애플리케이션 개발이 가능하다. 이 조합만으로도 충분히 의미가 있지만 여기에 Scala라는 약간은 불편한 듯 보이는 언어를 도입함으로써 얻을 수 있는 즐거움을 공유한다. Spring-Boot + JPA 조합에 Scala를 적용하면서의 좌충우돌 경험담을 전한다.
TDD 테스트 주도 개발이며, 하나의 개발 방법론 입니다.
- TDD는 반복 테스트을 이용한 소프트웨어 개발법이다. 작은 단위의 테스트 케이스를 작성하고 이를 통과하는 코드를 추가하는 단계를 반복하여 소프트웨어를 구현한다.
- TDD의 목표는 작동하는 깔끔한 코드 “Clean code that works”
- TDD는 아래 단계의 반복으로 진행된다.
빨강 : 실패하는 작은 테스트 케이스를 작성한다. 처음에는 컴파일조차 안될 수 있다.
초록 : 테스트를 통과하는 코드를 작성한다.
리펙터링 : 테스트를 통과하기 위해 만든 코드의 모든 중복을 제거하고, 불명확한 것을 명확히 한다.
이러한 단계로 인해 TDD는 “업무 코드 작성 전에 테스트 코드를 먼저 만드는 것”으로 정의되기도 한다
4. 테스트 종류
• 유닛 테스트 - 작성이 쉽다 , 로직의 버그와 디자인
문제 ( 단일 책임 원칙 , 하드 코딩 ) 를 찾는다
• 통합 테스트 – 객체 간 , 서비스 간 , 서브 시스템
간 상호 작용 테스트
• 기능 테스트 – 공개된 api 의 가장 바깥 부분 테스
트 , 유스케이스 단위 테스트
• 스트레스 테스트 – jMeter 등의 전용 도구 이용
• 인수 테스트 – 개발자가 아닌 고객 혹은 QA 에서 진
행
6. 유닛 테스트
“ 유닛 테스트 (unit test) 는 소스 코드가
의도된 대로 정확히 작동하는지 검증하
는 절차다 . 즉 , 모든 클래스와 메소드
에 대한 테스트 케이스를 작성하는 절차
를 말한다 .”
wikipedia.org
7. Why Unit Test
• 디버깅은 많은 시간을 소비하고 싶지 않
아
• 새로운 기능 추가나 리팩토링 후 기존
기능들이 잘 동작하는지 확신하고 싶어
• 테스트 코드를 읽고 class 의 동작을 명
확하게 이해할 수 있지
• 유닛 테스트를 통해 프로젝트 헬스와 코
드 퀄리티를 측정할 수 있거든
8.
9. Why not Unit Test
• 난 절대 실수 하지 않아
• 기능이 너무 단순해
• 테스트 코드까지 만들 시간이 없어
• 테스트 하는 방법을 몰라 ㅠㅠ
10. 유닛 테스트 특징
• Isolated
– 타이어 테스트 할 때 자동차까지 만들지 말자
• Repeatable
– 모든 개발자에서 테스트 가능
– 환경에 영향 받지 않게
• Fast
– 시간은 돈이다 .
– 쉽고 빠르게 테스트 코드를 만들 수 있어야 한다
• Self-Documenting
– 테스트 코드는 단순해서 이해하기 쉬워야 한다
– 테스트 코드를 설명하는 문서가 필요 없어야 한다
11. First Unit Test
Public class Calculator {
public double add(double number1, double number2) {
return number1 + number2;
}
}
import static org.junit.Assert.*;
import org.junit.Test;
public class CalculatorTest { 1.public class
@Test 2.unit test
public void test() {
Calculator calc = new Calculator();
double result = calc.add(10, 20); 3. 대상메소드콜
assertEquals(30, result, 0); 4. 결과 확인
}
}
19. Database Test 어려움
• Isolated
– DB 는 외부에 있다
• Repeatable
– Test 할 때마다 DB 가 변경된다
• Fast
– DB 접속은 상대적으로 느리다
– DB 접속 코드는 복잡하고 어렵다
• 초기 데이터 입력
• 평가 코드 작성
20. Database Test 전략
• Embedded DB(H2, HSQL) 사용
– Fast
– 개발자 별로 독립적 실행 가능
• DbUnit 사용
– 테스트 코드 작성 용이
– 쉬운 초기 데이터 셋팅
– 쉬운 평가 방법 제공
21. Database Test 대상
• 클래스와 테이블간 맵핑 오류
– DB 예약어 사용 예 )user, index, unique,
max
– 제품 DB 변경 시 활용
• 테이블 릴레이션
– one-to-many 등에서 이상한 forign 키 관계
가 없는지 ?
– Cascade 가 잘 동작하는지 ?
• 조회 쿼리
– 단일 객체 반환을 원하는데 복수 객체가 리턴
되지 않는지 ?
22. Database Test Code
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/META-INF/spring/test-applicationContext.xml")
@Transactional
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class
,
TransactionDbUnitTestExecutionListener.class,
DbUnitTestExecutionListener.class })
public class ServerDaoTest {
@Test
@DatabaseSetup("testServer.xml") 1. 초기 데이터
@ExpectedDatabase(value="empty.xml", 2. 기대 데이터 검증
assertionMode=DatabaseAssertionMode.NON_STRICT)
public void testDeleteServer() {
Server server = serverDao.getById(1);
serverDao.delete(server); 3. 테스트 대상
serverDao.flush(); 4. ORM 캐시 비우기
assertThat(serverDao.findAll().size(), is(0)); 5. delete 검증
}
23. Service 계층 Test 어려움
• Isolated
– 다른 서비스 / 계층 (DAO) 이 연관되어 있다
– 다른 시스템 (Agent, Mail Server, REST)
과 연관되어 있다
• Repeatable
– Test 할 때마다 모든 환경을 구축하기 어렵
다
• Fast
– 테스트 코드 작성시 많은 노력이 필요하다
24. Service 계층 Test 전략
• Stub 객체 작성
– Stub 이란 실제 대상과 유사하게 동작하는
객체 (Agent 시뮬레이터 )
– 동작 방식을 stub 객체에 코딩한다
– Stub 제작의 어려움이 남아 있다
• Mock 객체 이용
– Mock 이란 실제 객체와 유사한 동작을 하지
만 시키는
25. Service 계층 Test 전략
• Mock 객체 이용
– Mock 이란 실제 객체를 흉내내는 객체
– 동작 방식은 외부에서 알려준다 .
– Mock Framework 활용한 손쉬운 작성
• Mockito, EasyMock, Jmock
26. Service 계층 Test 코드
@Service
public class ServerServiceImpl implements ServerService {
@Autowired
ServerDao serverDao;
@Transactional
public Server discoveryServer(String ipAddress) {
Server server = serverDao.findServerByIpAddress(ipAddress);
if(server != null) {
throw new ServerDuplicatedException("Duplicated ipAddres=" + ipAddress);
}
Server newServer = new Server();
newServer.setIpAddress(ipAddress);
serverDao.save(newServer);
return newServer;
}
27. Service 계층 Test 코드
@RunWith(MockitoJUnitRunner.class) 1. mockito 러너 사용
public class ServerServiceTest {
@InjectMocks 2. mock 주입
ServerService serverService = new ServerServiceImpl();
@Mock 3. mock 객체 생성
ServerDao serverDao;
@Test
public void testDiscoveryServer() {
String ipAddress = "127.0.0.1";
//stub 4. mock 동작 정의
when(serverDao.findServerByIpAddress(ipAddress)).thenReturn(null);
//run
Server server=serverService.discoveryServer(ipAddress); 5. 대상 메소드 실행
//assert
assertThat(server.getIpAddress(), equalTo(ipAddress)); 6. 메소드 리턴 결과 판
정
verify(serverDao).findServerByIpAddress(ipAddress); 7. mock 호출 여부 확인
verify(serverDao).save(server); 8. mock 호출 여부 확인
}
28. Service 계층 Test 코드
@Test(expected=ServerDuplicatedException.class) 1. 예외 기대 및 판정
public void testDiscoveryServerDuplicated() {
String ipAddress = "127.0.0.1";
Server existServer = new Server();
when(serverDao.findServerByIpAddress(ipAddress)).thenReturn(existServer); 2.
mock 동작
Server server = serverService.discoveryServer(ipAddress); 3. 테스트 메
소드 실행
}
29. Web 계층 test 어려움
• 컴파일 타임에 문법 오류를 잡을 수 없다
– template 파일 , javascript 등
• 페이지 / 컴포넌트간 링크가 잘 동작하는 테
스트기 어렵다
• 국제화나 validate 등의 리소스 버그를 테
스트 하기 어렵다
• Isolate
– 서비스 계층이 구현되지 않은 경우 테스트가
어렵다
• Fast
– 웹 어플리케이션 기동 후 육안 검사에 의존
30. Web 계층 테스트 전략
• 서비스 계층을 mock 객체로 활용
• Tapestry Test framework 활용
– PageTester.renderPage()
– PageTester.clickLink()
• Selenium 테스트 도입
31. Web 계층 테스트 코드
public class ViewServerTest {
PageTester tester;
@Before 1. test setUp
public void setUp() {
String appPackage = "com.nkia.cygnus.management.server";
String appName = "development";
tester = new PageTester(appPackage, appName, "src/main/webapp",
TestAppModule.class);
}
@Test
public void testExistServer() {
ServerService serverService = tester.getService(ServerService.class); 2.
MockServerServic
when(serverService.getServerById(1)).thenReturn(newServer()); 3. Mock 동
작 정의
Document doc = tester.renderPage("server/ViewServer/1"); 4. 대상 페이지 렌더링
assertThat(doc.toString(),
containsString("View Server - testserver123456")); 4. 정상 여부
판정
32. Web 계층 테스트 코드
// link test
@Test
public void testDeleteNotExistServer() {
ServerService serverService = tester.getService(ServerService.class); 1. Mock
객체
when(serverService.findServersAll()).thenReturn(newServerList()); 2. Mock 동
작 정의
when(serverService.getServerById(1)).thenReturn(null);
Document doc = tester.renderPage("server/ServerPage"); 3. page rendering
Element delete = doc.getElementById("delete"); 4. link 객체 얻기
assertThat(delete, notNullValue()); 5. link 존재 검증
Document linkDoc = tester.clickLink(delete); 5. link 클릭
assertThat(linkDoc.toString(),
containsString("Server not found. id = 1")); 5. link 동작
검증
}
33. Test 커버리지 보고서
• 소스의 단위 테스트 커버리지 측정
• Cobertura 활용
• 테스트 커버리지 기준을 만들고 일정 수준
이 되어야 만 릴리즈 할 수 있는 정책 가능
– http://cms.nkia.net:8088/projects/cygnus/cygnu
• 클래스 복잡도 측정 - McCabe's
cyclomatic complexity)
– http://blog.wisedog.net/110