• Save
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)

  • 6,928 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
6,928
On Slideshare
6,816
From Embeds
112
Number of Embeds
6

Actions

Shares
Downloads
31
Comments
0
Likes
10

Embeds 112

http://blog.doortts.com 86
http://doortts.tistory.com 20
http://www.hanrss.com 3
http://static.slidesharecdn.com 1
http://webcache.googleusercontent.com 1
http://blog.naver.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 테스트 가능한 소프트웨어 설계와 TDD작성 패턴 Testable software design & TDD patterns 한국 스프링 사용자 모임 (KSUG ) 채수원
  • 2. 발표자 개 발 자 소개 LG CNS 경영기술교육원 기술교육팀 전임강사 강의 과목 디자인 패턴 & 리팩터링 분석설계 실무 Agile A il 적용실무 블로그 여름으로 가는 문 blog.doortts.com Comments 내용이나 후기에 대해서는 Outsider님의 후기 (http://blog.outsider.ne.kr/494) 를 참조 하시면 (htt //bl t id k /494) 좀 더 도움이 될 겁니다.
  • 3. 흠 흠… Comments 2010년 7월 10일 화창한 토요일 오후 이대 ECC
  • 4. Why are we here?
  • 5. 보다 더 나은 소프트웨어와 다 웨어와 보다 더 나은 삶을 만들기 위해
  • 6. 객체 지향 기본 원칙
  • 7. OCP SRP ISP Demeter s Demeter’s Law (=hollywood law) ( hollywood IOC Comments 언어도 열심히 배우고 원칙도 학습했으니까 개발을 더 잘할 수 있겠죠?
  • 8. 다음 두 코드 중 더 나은 디자인은? Case.1 class Rental { Movie movie; Rental(Service service) { this.movie = service.getMovie(); } } Case.2 C 2 class R t l { Rental Movie movie; Rental(Movie movie) { this.movie = movie; } }
  • 9. (based on my five years of educational experience) Strongly recommended approach #1 St l d d h 테스트 주도 개발 Test-Driven D T t Di Development l t
  • 10. TDD 관점에서 바라봤을 때 드러나는 안 좋은 디자인의 징후 - 단위 테스트 케이스 작성이 어렵다 - 단위 테스트 케이스가 자주 깨진다 깨진다. - 단위 테스트 케이스 실행을 위한 준비해야 할 것이 많다. - 다른 사람의 테스트 케이스를 읽기가 어렵다.
  • 11. 강형 마이크로 디자인
  • 12. 기초 점검 코스
  • 13. “프로그램을 작성하기 전에 테스트 먼저 하라!” 프로그램을 하라! Test the program before you write it. “잘 동작하는 깔끔한 코드” 잘 Clean code that works “질문 질 응답 정 정제 반복 반복” Ask Respond Refine Repeat
  • 14. p public class Calculator { public int sum(int a, int b) { return 0; } public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println( calc.sum(10, 20) == 30 ); System.out.println( calc.sum(1, 2) == 3 ); System.out.println( calc.sum(-10, 20) == 10 ); S i l ( l ( 10 ) System.out.println( calc.sum(0, 0) == 0 ); } } 모두 true면 작성완료! -----실행 결과----- 실행 결과 false false false Comments true 굳이 프레임워크를 쓰지 않아도 무방합니다. 업무로직 작성 전에 완성상태를 검증해 줄 수 있는 코드가 존재하기만 하면 충분합니다.
  • 15. 기본 코스
  • 16. 생성자 메소드 테스트 ( (constructor method test) ) public class E l bli l EmployeeDaoTest { D T t @Test public EmployeeDaoTest { EmployeeDao d = new E l E l D dao EmployeeDao(); D () assertTrue(dao.isConnected()); }
  • 17. 동치 비교 ( q (equivalence test) ) @Test public void testEquals_case2() { Music musicA = new M i ("BAD" "Mi h l") i i Music("BAD", "Michael"); Music musicB = new Music("BAD", "Michael"); assertEquals ( musicA musicB); musicA, }
  • 18. 동치 비교 ( q (equivalence test) ) 해결책.1 해결책 1 내부 상태(보통은 필드값)를 직접 꺼내와서 각각 비교한다. 해결책.2 toString을 중첩구현해(override)놓고, toString 값 으로 비교한다. 해결책.3 equals 메소드를 중첩구현한다. 해결책.3 Unitils의 assertReflectionEquals를 이용한다.
  • 19. 배열비교 ( (array test) y ) 해결책.1 해결책 1 JUnit 4의 assertArrayEquals를 이용한다 이용한다. 해결책.2 Unitils의 assertReflectionEquals나 assertLenientEquals를 이용한다 해결책.3 List로 변환해서 비교한다.
  • 20. 배열비교 ( (array test) y ) @Test public void testArrayEqual_NotSorted() { String[] arrayA = new String[] {"A", "B", "C"}; { A B C }; String[] arrayB = new String[] {"B", "A", "C"}; Arrays.sort Arrays sort (arrayA); Arrays.sort (arrayB); assertArrayEquals (arrayA, arrayB); }
  • 21. 몇 가지 오해
  • 22. boolean isRight(){ return TDD == UnitTest }
  • 23. (Do) All or Noting
  • 24. 단위 테스트 케이스 작성
  • 25. Skeleton vs Incremental package main; public class Account { public Account(int i) { } public i bli int getBalance() { l () return 0; } public void withdraw(){ } public void deposit(){ } }
  • 26. One method one assert ? @Test public void testGetBalance() throws Exception { bli id l () h i assertEquals (10000, account.getBalance()); account = new Account(1000); assertEquals (1000, account.getBalance()); account = new Account(0); assertEquals (0, account.getBalance()); }
  • 27. Anti-pattern Anti pattern private Account account; @Before public void setUp(){ account = new Account(10000); } @Test public void testWithdraw() throws Exception { account.withdraw(1000); ( ) assertEquals(9000, account.getBalance()); }
  • 28. Anti-pattern public class AccountTest { @Before public void setUp(){ } @Test p public void testDeposit() throws Exception { p () p Account account = new Account(10000); account.deposit(1000); assertEquals(11000, account.getBalance()); } @Test public void testWithdraw() throws Exception { Account account = new Account(10000); account.withdraw(1000); assertEquals(9000, account.getBalance()); l ( l ()) } }
  • 29. 단위 테스트 접근 방식
  • 30. 상태기반 테스트 입력 methodA doSomething ? 예상결과 = 실제결과
  • 31. 상태기반 테스트 @Test public void t tFil R testFileRemove() { () FileUtile fileUtil = new FileUtil(); fileUtil.cleanContents( targetFile ); assertEquals( 0 fileUtil size( targetFile ) ); 0, fileUtil.size( }
  • 32. 행위기반 테스트 Case.1 Case 1 입력A methodA methodB doSomething rampOn Case.2 입력B methodA methodB call doSomething d S thi rampOn O
  • 33. 행위기반 테스트 @Test public void testGetOrderPrice () throws Exception { PriceCalculator calculator = new PriceCalculator(); Item item = new Item("LightSavor","Kitchen knife",100000); ICoupon coupon = new Coupon(); assertEquals(93000, calculator.getOrderPrice(item, coupon)); int methodCallCount = ((Coupon)coupon).getIsAppliableCallCount(); assertEquals (1, methodCallCount); }
  • 34. 행위기반 테스트 public class Coupon implements ICoupon { private int isAppliableCallCount; @Override public boolean isAppliable(Item item) { isAppliableCallCount++; // 호출되면 증가 출 …….. } p public int getIsAppliableCallCount(){ g pp (){ return this.isAppliableCallCount; } }
  • 35. 행위기반 테스트 @Test p public void testGetOrderPrice () throws Exception { p PriceCalculator calculator = new PriceCalculator(); Item item = new Item("LightSavor","Kitchen knife",100000); Item( LightSavor Kitchen knife 100000); ICoupon mockCoupon = mock(ICoupon.class); … // mocking 작업 assertEquals(93000, calculator.getOrderPrice(item, mockCoupon)); verify ( if (mockCoupon, ti kC times(1)).isAppliable(box); (1)) i A li bl (b ); }
  • 36. TDD with Spring 스프링 프레임워크의 Unit Test 지원 링 레임워 의 - 의존관계 주입을 통한 객체 생성 - 웹 컨테이너 없는 웹 애플리케이션 테스트 - 단위 테스트 지원 유틸리티 => Injection과 Mock
  • 37. XmlBeanFacotry로 컨텍스트 가져오는 버전 y 텍 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi http://www.w3.org/2001/XMLSchema instance xmlns:xsi="http://www w3 org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http //www.springframework.org/schema/beans/spring beans.xsd > http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="music" class="main.MP3"> <constructor-arg value="Belong to me.mp3"/> g g p </bean> <bean id="musicPlayer" class="main.MusicPlayer"> <property name="music" ref="music"/> </bean> </beans> XmlBeanFactory beanFactory = new XmlBeanFactory( new ClassPathResource("/context-musicplayer xml") ClassPathResource( /context musicplayer.xml ) );
  • 38. Unitils를 사용 @RunWith(UnitilsJUnit4TestClassRunner class) @RunWith(UnitilsJUnit4TestClassRunner.class) public class UnitilsMusicPlayerTest { @SpringApplicationContext( /context-musicplayer.xml ) @SpringApplicationContext("/context-musicplayer xml") private ApplicationContext context; … … }
  • 39. annotation및 autowire를 사용해서 @RunWith(SpringJUnit4ClassRunner class) @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"/context-musicplayer.xml"}) public class AutowiredMusicPlayerTest { @Autowired MusicPlayer player; … … }
  • 40. Injection기능만 사용할 경우 Google Guice(쥬스)로 처리해 보면 static Injector injector = Guice.createInjector(new MusicModule()); @Test public void testGetFileName() throws Exception { MusicPlayer player = injector.getInstance(MusicPlayer.class); assertEquals("Belong To Me.mp3", player.getFileName()); }
  • 41. TDD with Spring – servlet test 이름 박성철 사번 5874 아이디 fupfin 직위 회장
  • 42. TDD with Spring – servlet test public class EmployeeSearchServletTest { @Test public void testSearchByEmpid() throws Exception { bli id S h id() h i MockHttpServletRequest request = new MockHttpServletRequest(); // ➊ MockHttpServletResponse response = new MockHttpServletResponse(); // ➋ request.addParameter("empid", "5874") // ➌ ddP (" id" "5874"); EmployeeSearchServlet searchServlet = new EmployeeSearchServlet(); // ➍ searchServlet.service(request, response); // ➎ Employee employee = (Employee)request getAttribute("employee"); // ➏ (Employee)request.getAttribute( employee ); assertEquals ("박성철", employee.getName() ); // ➐ assertEquals ("5874", employee getEmpid() ); ( 5874 employee.getEmpid() assertEquals ("fupfin", employee.getId() ); assertEquals ("회장", employee.getPosition() ); assertEquals( /SearchResult.jsp , assertEquals("/SearchResult.jsp", response.getForwardedUrl()); // ➑ } }
  • 43. 발표를 마치며
  • 44. Q&A 감사합니다 doortts@gmail.com
  • 45. 이미지 참조 Longing for Summer http://www.flickr.com/photos/68165632@N00/1553091251/ Coupling sketches cropped http://www.flickr.com/photos/49432745@N03/4533798684/ Tagged, finally... http://www.flickr.com/photos/kornrawiee/3189034267/ Vincent Boiteau i i http://www.flickr.com/photos/2dogs_productions