cygnus unit test

33
Cygnus Unit Test 전전전전전 전전전

Upload: sung-jae-park

Post on 21-Dec-2014

1.014 views

Category:

Technology


2 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Cygnus unit test

Cygnus Unit Test

전략개발팀 박성재

Page 2: Cygnus unit test

Agenda

• Test?• TDD• Database Test• Service Tier Test• Web Tier Test• Test Coverage

Page 3: Cygnus unit test

인수 테스트

스트레스 /부하 테스트

기능 테스트

통합 테스트

테스트 종류

유닛 테스트

Page 4: Cygnus unit test

테스트 종류• 유닛 테스트 - 작성이 쉽다 , 로직의 버그와 디자인

문제 ( 단일 책임 원칙 , 하드 코딩 ) 를 찾는다

• 통합 테스트 – 객체 간 , 서비스 간 , 서브 시스템 간 상호 작용 테스트

• 기능 테스트 – 공개된 api 의 가장 바깥 부분 테스트 , 유스케이스 단위 테스트

• 스트레스 테스트 – jMeter 등의 전용 도구 이용

• 인수 테스트 – 개발자가 아닌 고객 혹은 QA 에서 진행

Page 5: Cygnus unit test

Mockito

Tapestry Test

Test 관련 Tools

Spring Test

Page 6: Cygnus unit test

유닛 테스트

“ 유닛 테스트 (unit test) 는 소스 코드가 의도된 대로 정확히 작동하는지 검증하는 절차다 . 즉 , 모든 클래스와 메소드에 대한 테스트 케이스를 작성하는 절차를 말한다 .”

wikipedia.org

Page 7: Cygnus unit test

Why Unit Test

• 디버깅은 많은 시간을 소비하고 싶지 않아• 새로운 기능 추가나 리팩토링 후 기존

기능들이 잘 동작하는지 확신하고 싶어• 테스트 코드를 읽고 class 의 동작을

명확하게 이해할 수 있지• 유닛 테스트를 통해 프로젝트 헬스와 코드

퀄리티를 측정할 수 있거든

Page 8: Cygnus unit test
Page 9: Cygnus unit test

Why not Unit Test

• 난 절대 실수 하지 않아• 기능이 너무 단순해• 테스트 코드까지 만들 시간이 없어

• 테스트 하는 방법을 몰라 ㅠㅠ

Page 10: Cygnus unit test

유닛 테스트 특징• Isolated– 타이어 테스트 할 때 자동차까지 만들지 말자

• Repeatable– 모든 개발자에서 테스트 가능– 환경에 영향 받지 않게

• Fast– 시간은 돈이다 .– 쉽고 빠르게 테스트 코드를 만들 수 있어야 한다

• Self-Documenting– 테스트 코드는 단순해서 이해하기 쉬워야 한다– 테스트 코드를 설명하는 문서가 필요 없어야 한다

Page 11: Cygnus unit test

First Unit TestPublic 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. 결과 확인 }}

Page 12: Cygnus unit test

First Unit Test 실행

Page 13: Cygnus unit test

Test Driven Development(TDD)

“ 좋은 코드는 테스트하기 쉽다 . 그 반대도 마찬가지다 .”

Page 14: Cygnus unit test
Page 15: Cygnus unit test
Page 16: Cygnus unit test
Page 17: Cygnus unit test
Page 18: Cygnus unit test

Cygnus Tier Flow

CreateServer

ServerService

ServerDao

saveServer(server)

saveServer(server)doSomething(server)

session.save(server)

ViewServer

ServerService

ServerDao

getServerById(id)

getServerById(id)

session.get(Server.class, id)

http://.../viewserver/{id}

Server

Web Tier :

Service Tier :

Dao Tier :

Persistent Tier :

Page 19: Cygnus unit test

Database Test 어려움

• Isolated– DB 는 외부에 있다

• Repeatable– Test 할 때마다 DB 가 변경된다

• Fast– DB 접속은 상대적으로 느리다– DB 접속 코드는 복잡하고 어렵다• 초기 데이터 입력• 평가 코드 작성

Page 20: Cygnus unit test

Database Test 전략

• Embedded DB(H2, HSQL) 사용– Fast–개발자 별로 독립적 실행 가능

• DbUnit 사용–테스트 코드 작성 용이–쉬운 초기 데이터 셋팅 –쉬운 평가 방법 제공

Page 21: Cygnus unit test

Database Test 대상

• 클래스와 테이블간 맵핑 오류– DB 예약어 사용 예 )user, index, unique, max– 제품 DB 변경 시 활용

• 테이블 릴레이션– one-to-many 등에서 이상한 forign 키 관계가

없는지 ?– Cascade 가 잘 동작하는지 ?

• 조회 쿼리– 단일 객체 반환을 원하는데 복수 객체가 리턴되지

않는지 ?

Page 22: Cygnus unit test

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 검증 }

Page 23: Cygnus unit test

Service 계층 Test 어려움

• Isolated–다른 서비스 /계층 (DAO) 이 연관되어 있다–다른 시스템 (Agent, Mail Server, REST) 과

연관되어 있다• Repeatable– Test 할 때마다 모든 환경을 구축하기 어렵다

• Fast–테스트 코드 작성시 많은 노력이 필요하다

Page 24: Cygnus unit test

Service 계층 Test 전략

• Stub 객체 작성– Stub 이란 실제 대상과 유사하게 동작하는

객체 (Agent 시뮬레이터 )–동작 방식을 stub 객체에 코딩한다– Stub 제작의 어려움이 남아 있다

• Mock 객체 이용–Mock 이란 실제 객체와 유사한 동작을 하지만

시키는

Page 25: Cygnus unit test

Service 계층 Test 전략

• Mock 객체 이용–Mock 이란 실제 객체를 흉내내는 객체–동작 방식은 외부에서 알려준다 .–Mock Framework 활용한 손쉬운 작성• Mockito, EasyMock, Jmock

Page 26: Cygnus unit test

Service 계층 Test 코드@Servicepublic 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; }

Page 27: Cygnus unit test

Service 계층 Test 코드@RunWith(MockitoJUnitRunner.class) 1. mockito 러너 사용public class ServerServiceTest {@InjectMocks 2. mock 주입ServerService serverService = new ServerServiceImpl();@Mock 3. mock 객체 생성ServerDao serverDao;

@Testpublic 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 호출 여부 확인}

Page 28: Cygnus unit test

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. 테스트 메소드 실행}

Page 29: Cygnus unit test

Web 계층 test 어려움 • 컴파일 타임에 문법 오류를 잡을 수 없다– template 파일 , javascript 등

• 페이지 /컴포넌트간 링크가 잘 동작하는 테스트기 어렵다

• 국제화나 validate 등의 리소스 버그를 테스트 하기 어렵다

• Isolate– 서비스 계층이 구현되지 않은 경우 테스트가

어렵다• Fast– 웹 어플리케이션 기동 후 육안 검사에 의존

Page 30: Cygnus unit test

Web 계층 테스트 전략

• 서비스 계층을 mock 객체로 활용• Tapestry Test framework 활용– PageTester.renderPage()– PageTester.clickLink()

• Selenium 테스트 도입

Page 31: Cygnus unit test

Web 계층 테스트 코드public class ViewServerTest {PageTester tester;

@Before 1. test setUppublic void setUp() { String appPackage = "com.nkia.cygnus.management.server"; String appName = "development"; tester = new PageTester(appPackage, appName, "src/main/webapp", TestAppModule.class);}

@Testpublic 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. 정상 여부 판정}

Page 32: Cygnus unit test

Web 계층 테스트 코드// link test@Testpublic 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 동작 검증}

Page 33: Cygnus unit test

Test 커버리지 보고서

• 소스의 단위 테스트 커버리지 측정• Cobertura 활용• 테스트 커버리지 기준을 만들고 일정 수준이

되어야 만 릴리즈 할 수 있는 정책 가능– http://cms.nkia.net:8088/projects/cygnus/c

ygnus-management-server/cobertura/index.html

• 클래스 복잡도 측정 - McCabe's cyclomatic complexity)– http://blog.wisedog.net/110