테스트 가능한
소프트웨어 설계와
TDD작성 패턴
Testable software design & TDD patterns




                         한국 스프링 사용자 모임 (KSUG )

   ...
발표자 개
발 자 소개
LG CNS 경영기술교육원 기술교육팀 전임강사

강의 과목
디자인 패턴 & 리팩터링
분석설계 실무
Agile
A il 적용실무

블로그
여름으로 가는 문 blog.doortts.com



Com...
흠
흠…

     Comments
       2010년 7월 10일 화창한 토요일 오후 이대 ECC
Why are we here?
보다 더 나은 소프트웨어와
 다         웨어와
보다 더 나은 삶을 만들기 위해
객체 지향 기본 원칙
OCP
SRP
ISP
Demeter s
Demeter’s Law (=hollywood law)
              ( hollywood
IOC
        Comments
        언어도 열심히 배우고 원칙...
다음 두 코드 중 더 나은 디자인은?
 Case.1   class Rental {
              Movie movie;
              Rental(Service service) {
         ...
(based on my five years of educational experience)

Strongly recommended approach #1
St    l          d d        h




   ...
TDD 관점에서 바라봤을 때 드러나는



     안 좋은 디자인의 징후
      - 단위 테스트 케이스 작성이 어렵다
      - 단위 테스트 케이스가 자주 깨진다
                       깨진다...
강형 마이크로 디자인
기초 점검 코스
“프로그램을 작성하기 전에 테스트 먼저 하라!”
 프로그램을                하라!
Test the program before you write it.




“잘 동작하는 깔끔한 코드”
 잘
Clean co...
p
public class Calculator {
  public int sum(int a, int b) {
    return 0;
  }

    public static void main(String[] args)...
기본 코스
생성자 메소드 테스트
(
(constructor method test)
                        )

 public class E l
   bli l      EmployeeDaoTest {
     ...
동치 비교
( q
(equivalence test)
                 )

@Test
public void testEquals_case2() {
    Music musicA = new M i ("BAD" ...
동치 비교
( q
(equivalence test)
                 )
해결책.1
해결책 1   내부 상태(보통은 필드값)를 직접 꺼내와서 각각
        비교한다.
해결책.2   toString을 중...
배열비교
(
(array test)
     y     )
해결책.1
해결책 1   JUnit 4의 assertArrayEquals를 이용한다
                                    이용한다.
...
배열비교
(
(array test)
     y     )

@Test
public void testArrayEqual_NotSorted() {
    String[] arrayA = new String[] {"A", ...
몇 가지 오해
boolean isRight(){
    return TDD == UnitTest
}
(Do) All or Noting
단위 테스트 케이스 작성
Skeleton vs Incremental
      package main;
      public class Account {

          public Account(int i) {
          }
  ...
One method one assert ?
  @Test
  public void testGetBalance() throws Exception {
    bli    id          l    () h        ...
Anti-pattern
Anti pattern
      private Account account;

      @Before
      public void setUp(){
          account = new...
Anti-pattern
    public     class AccountTest {

            @Before
            public void setUp(){
            }

     ...
단위 테스트 접근 방식
상태기반 테스트
                 입력



       methodA


           doSomething



            ?


     예상결과
          = 실제결과
상태기반 테스트

 @Test
 public void t tFil R
             testFileRemove() {
                           ()
     FileUtile fileUt...
행위기반 테스트
  Case.1
  Case 1      입력A


           methodA               methodB


            doSomething          rampOn

...
행위기반 테스트

@Test
public void testGetOrderPrice () throws Exception {
    PriceCalculator calculator = new PriceCalculator()...
행위기반 테스트
public class Coupon implements ICoupon {

    private int isAppliableCallCount;
    @Override
    public boolean ...
행위기반 테스트

@Test
p
public void testGetOrderPrice () throws Exception {
                                            p
    Pr...
TDD with Spring
  스프링 프레임워크의 Unit Test 지원
    링 레임워 의


     - 의존관계 주입을 통한 객체 생성
     - 웹 컨테이너 없는 웹 애플리케이션 테스트
     - 단위 테...
XmlBeanFacotry로 컨텍스트 가져오는 버전
             y   텍
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springfram...
Unitils를 사용

 @RunWith(UnitilsJUnit4TestClassRunner class)
 @RunWith(UnitilsJUnit4TestClassRunner.class)
 public class Uni...
annotation및 autowire를 사용해서

@RunWith(SpringJUnit4ClassRunner class)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfig...
Injection기능만 사용할 경우
Google Guice(쥬스)로 처리해 보면

static Injector injector = Guice.createInjector(new MusicModule());

@Test
p...
TDD with Spring – servlet test




           이름     박성철
           사번     5874

           아이디    fupfin

           직위  ...
TDD with Spring – servlet test
public class EmployeeSearchServletTest {
@Test
public void testSearchByEmpid() throws Excep...
발표를 마치며
Q&A
    감사합니다

doortts@gmail.com
이미지 참조
Longing for Summer
http://www.flickr.com/photos/68165632@N00/1553091251/

Coupling sketches cropped
http://www.flic...
Upcoming SlideShare
Loading in...5
×

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

8,866

Published on

0 Comments
120 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
8,866
On Slideshare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
31
Comments
0
Likes
120
Embeds 0
No embeds

No notes for slide

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

  1. 1. 테스트 가능한 소프트웨어 설계와 TDD작성 패턴 Testable software design & TDD patterns 한국 스프링 사용자 모임 (KSUG ) 채수원
  2. 2. 발표자 개 발 자 소개 LG CNS 경영기술교육원 기술교육팀 전임강사 강의 과목 디자인 패턴 & 리팩터링 분석설계 실무 Agile A il 적용실무 블로그 여름으로 가는 문 blog.doortts.com Comments 내용이나 후기에 대해서는 Outsider님의 후기 (http://blog.outsider.ne.kr/494) 를 참조 하시면 (htt //bl t id k /494) 좀 더 도움이 될 겁니다.
  3. 3. 흠 흠… Comments 2010년 7월 10일 화창한 토요일 오후 이대 ECC
  4. 4. Why are we here?
  5. 5. 보다 더 나은 소프트웨어와 다 웨어와 보다 더 나은 삶을 만들기 위해
  6. 6. 객체 지향 기본 원칙
  7. 7. OCP SRP ISP Demeter s Demeter’s Law (=hollywood law) ( hollywood IOC Comments 언어도 열심히 배우고 원칙도 학습했으니까 개발을 더 잘할 수 있겠죠?
  8. 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. 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. 10. TDD 관점에서 바라봤을 때 드러나는 안 좋은 디자인의 징후 - 단위 테스트 케이스 작성이 어렵다 - 단위 테스트 케이스가 자주 깨진다 깨진다. - 단위 테스트 케이스 실행을 위한 준비해야 할 것이 많다. - 다른 사람의 테스트 케이스를 읽기가 어렵다.
  11. 11. 강형 마이크로 디자인
  12. 12. 기초 점검 코스
  13. 13. “프로그램을 작성하기 전에 테스트 먼저 하라!” 프로그램을 하라! Test the program before you write it. “잘 동작하는 깔끔한 코드” 잘 Clean code that works “질문 질 응답 정 정제 반복 반복” Ask Respond Refine Repeat
  14. 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. 15. 기본 코스
  16. 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. 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. 18. 동치 비교 ( q (equivalence test) ) 해결책.1 해결책 1 내부 상태(보통은 필드값)를 직접 꺼내와서 각각 비교한다. 해결책.2 toString을 중첩구현해(override)놓고, toString 값 으로 비교한다. 해결책.3 equals 메소드를 중첩구현한다. 해결책.3 Unitils의 assertReflectionEquals를 이용한다.
  19. 19. 배열비교 ( (array test) y ) 해결책.1 해결책 1 JUnit 4의 assertArrayEquals를 이용한다 이용한다. 해결책.2 Unitils의 assertReflectionEquals나 assertLenientEquals를 이용한다 해결책.3 List로 변환해서 비교한다.
  20. 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. 21. 몇 가지 오해
  22. 22. boolean isRight(){ return TDD == UnitTest }
  23. 23. (Do) All or Noting
  24. 24. 단위 테스트 케이스 작성
  25. 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. 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. 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. 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. 29. 단위 테스트 접근 방식
  30. 30. 상태기반 테스트 입력 methodA doSomething ? 예상결과 = 실제결과
  31. 31. 상태기반 테스트 @Test public void t tFil R testFileRemove() { () FileUtile fileUtil = new FileUtil(); fileUtil.cleanContents( targetFile ); assertEquals( 0 fileUtil size( targetFile ) ); 0, fileUtil.size( }
  32. 32. 행위기반 테스트 Case.1 Case 1 입력A methodA methodB doSomething rampOn Case.2 입력B methodA methodB call doSomething d S thi rampOn O
  33. 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. 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. 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. 36. TDD with Spring 스프링 프레임워크의 Unit Test 지원 링 레임워 의 - 의존관계 주입을 통한 객체 생성 - 웹 컨테이너 없는 웹 애플리케이션 테스트 - 단위 테스트 지원 유틸리티 => Injection과 Mock
  37. 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. 38. Unitils를 사용 @RunWith(UnitilsJUnit4TestClassRunner class) @RunWith(UnitilsJUnit4TestClassRunner.class) public class UnitilsMusicPlayerTest { @SpringApplicationContext( /context-musicplayer.xml ) @SpringApplicationContext("/context-musicplayer xml") private ApplicationContext context; … … }
  39. 39. annotation및 autowire를 사용해서 @RunWith(SpringJUnit4ClassRunner class) @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"/context-musicplayer.xml"}) public class AutowiredMusicPlayerTest { @Autowired MusicPlayer player; … … }
  40. 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. 41. TDD with Spring – servlet test 이름 박성철 사번 5874 아이디 fupfin 직위 회장
  42. 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. 43. 발표를 마치며
  44. 44. Q&A 감사합니다 doortts@gmail.com
  45. 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

×