SlideShare ist ein Scribd-Unternehmen logo
1 von 45
테스트 가능한
소프트웨어 설계와
TDD작성 패턴
Testable software design & TDD patterns




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

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

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

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



Comments
  내용이나 후기에 대해서는 Outsider님의 후기
  (http://blog.outsider.ne.kr/494) 를 참조 하시면
  (htt //bl      t id      k /494)
  좀 더 도움이 될 겁니다.
흠
흠…

     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) {
                  this.movie = service.getMovie();
              }
          }

 Case.2
 C    2   class R t l {
                Rental
              Movie movie;
              Rental(Movie movie) {
                  this.movie = movie;
              }
          }
(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
TDD 관점에서 바라봤을 때 드러나는



     안 좋은 디자인의 징후
      - 단위 테스트 케이스 작성이 어렵다
      - 단위 테스트 케이스가 자주 깨진다
                       깨진다.
      - 단위 테스트 케이스 실행을 위한 준비해야 할 것이 많다.
      - 다른 사람의 테스트 케이스를 읽기가 어렵다.
강형 마이크로 디자인
기초 점검 코스
“프로그램을 작성하기 전에 테스트 먼저 하라!”
 프로그램을                하라!
Test the program before you write it.




“잘 동작하는 깔끔한 코드”
 잘
Clean code that works




“질문
 질             응답            정
                             정제         반복
                                        반복”
Ask     Respond     Refine    Repeat
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       굳이 프레임워크를 쓰지 않아도 무방합니다. 업무로직 작성 전에 완성상태를
              검증해 줄 수 있는 코드가 존재하기만 하면 충분합니다.
기본 코스
생성자 메소드 테스트
(
(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());
 }
동치 비교
( 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,
}
동치 비교
( q
(equivalence test)
                 )
해결책.1
해결책 1   내부 상태(보통은 필드값)를 직접 꺼내와서 각각
        비교한다.
해결책.2   toString을 중첩구현해(override)놓고, toString 값
        으로 비교한다.

해결책.3   equals 메소드를 중첩구현한다.

해결책.3   Unitils의 assertReflectionEquals를 이용한다.
배열비교
(
(array test)
     y     )
해결책.1
해결책 1   JUnit 4의 assertArrayEquals를 이용한다
                                    이용한다.


해결책.2   Unitils의 assertReflectionEquals나
        assertLenientEquals를 이용한다

해결책.3   List로 변환해서 비교한다.
배열비교
(
(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);
}
몇 가지 오해
boolean isRight(){
    return TDD == UnitTest
}
(Do) All or Noting
단위 테스트 케이스 작성
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(){
          }
      }
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());
  }
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());
      }
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     ())
        }
    }
단위 테스트 접근 방식
상태기반 테스트
                 입력



       methodA


           doSomething



            ?


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

 @Test
 public void t tFil R
             testFileRemove() {
                           ()
     FileUtile fileUtil = new FileUtil();
     fileUtil.cleanContents( targetFile );
     assertEquals( 0 fileUtil size( targetFile ) );
                   0, fileUtil.size(
 }
행위기반 테스트
  Case.1
  Case 1      입력A


           methodA               methodB


            doSomething          rampOn




  Case.2      입력B


           methodA
                                 methodB

                          call
           doSomething
           d S   thi                 rampOn
                                         O
행위기반 테스트

@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);
}
행위기반 테스트
public class Coupon implements ICoupon {

    private int isAppliableCallCount;
    @Override
    public boolean isAppliable(Item item) {
        isAppliableCallCount++; // 호출되면 증가
                                     출
         ……..
    }

    p
    public int getIsAppliableCallCount(){
               g     pp               (){
        return this.isAppliableCallCount;
    }
}
행위기반 테스트

@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 );
}
TDD with Spring
  스프링 프레임워크의 Unit Test 지원
    링 레임워 의


     - 의존관계 주입을 통한 객체 생성
     - 웹 컨테이너 없는 웹 애플리케이션 테스트
     - 단위 테스트 지원 유틸리티


                   => Injection과 Mock
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 )
     );
Unitils를 사용

 @RunWith(UnitilsJUnit4TestClassRunner class)
 @RunWith(UnitilsJUnit4TestClassRunner.class)
 public class UnitilsMusicPlayerTest {

     @SpringApplicationContext( /context-musicplayer.xml )
     @SpringApplicationContext("/context-musicplayer xml")
     private ApplicationContext context;
 …
 …
 }
annotation및 autowire를 사용해서

@RunWith(SpringJUnit4ClassRunner class)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/context-musicplayer.xml"})
public class AutowiredMusicPlayerTest {

    @Autowired
    MusicPlayer player;
…
…
}
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());
}
TDD with Spring – servlet test




           이름     박성철
           사번     5874

           아이디    fupfin

           직위     회장
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()); // ➑
    }
}
발표를 마치며
Q&A
    감사합니다

doortts@gmail.com
이미지 참조
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

Weitere ähnliche Inhalte

Was ist angesagt?

Unit Testing And Mocking
Unit Testing And MockingUnit Testing And Mocking
Unit Testing And Mocking
Joe Wilson
 
Exploratory testing
Exploratory testingExploratory testing
Exploratory testing
Huib Schoots
 
Test automation using selenium
Test automation using seleniumTest automation using selenium
Test automation using selenium
shreyas JC
 

Was ist angesagt? (20)

(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
 
테스트자동화 성공전략
테스트자동화 성공전략테스트자동화 성공전략
테스트자동화 성공전략
 
테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례
 
Accessibility Testing using Axe
Accessibility Testing using AxeAccessibility Testing using Axe
Accessibility Testing using Axe
 
Handle Exceptions in Selenium Webdriver | Edureka
Handle Exceptions in Selenium Webdriver | EdurekaHandle Exceptions in Selenium Webdriver | Edureka
Handle Exceptions in Selenium Webdriver | Edureka
 
Exploratory Testing
Exploratory TestingExploratory Testing
Exploratory Testing
 
Rest api 테스트 수행가이드
Rest api 테스트 수행가이드Rest api 테스트 수행가이드
Rest api 테스트 수행가이드
 
Webdriver.io
Webdriver.io Webdriver.io
Webdriver.io
 
Py.test
Py.testPy.test
Py.test
 
Unit Testing And Mocking
Unit Testing And MockingUnit Testing And Mocking
Unit Testing And Mocking
 
API Testing following the Test Pyramid
API Testing following the Test PyramidAPI Testing following the Test Pyramid
API Testing following the Test Pyramid
 
Automated testing with Cypress
Automated testing with CypressAutomated testing with Cypress
Automated testing with Cypress
 
An Introduction to Unit Testing
An Introduction to Unit TestingAn Introduction to Unit Testing
An Introduction to Unit Testing
 
Exploratory testing
Exploratory testingExploratory testing
Exploratory testing
 
Introduction to Bdd and cucumber
Introduction to Bdd and cucumberIntroduction to Bdd and cucumber
Introduction to Bdd and cucumber
 
Unit tests & TDD
Unit tests & TDDUnit tests & TDD
Unit tests & TDD
 
Celery의 빛과 그림자
Celery의 빛과 그림자Celery의 빛과 그림자
Celery의 빛과 그림자
 
Bdd and spec flow
Bdd and spec flowBdd and spec flow
Bdd and spec flow
 
Test automation using selenium
Test automation using seleniumTest automation using selenium
Test automation using selenium
 
짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례
 

Andere mochten auch

Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017
Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017
Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017
Amazon Web Services Korea
 

Andere mochten auch (6)

테스트 자동화와 TDD(테스트 주도 개발방법론)
테스트 자동화와 TDD(테스트 주도 개발방법론)테스트 자동화와 TDD(테스트 주도 개발방법론)
테스트 자동화와 TDD(테스트 주도 개발방법론)
 
IoT 개발자를 위한 Embedded C에서 TDD를 해보자
IoT 개발자를 위한 Embedded C에서 TDD를 해보자IoT 개발자를 위한 Embedded C에서 TDD를 해보자
IoT 개발자를 위한 Embedded C에서 TDD를 해보자
 
Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017
Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017
Amazon ECS를 통한 도커 기반 콘테이너 서비스 구축하기 - AWS Summit Seoul 2017
 
콘테이너 운영을 위한 Amazon EC2 Container Service(ECS) 집중 분석
콘테이너 운영을 위한 Amazon EC2 Container Service(ECS) 집중 분석콘테이너 운영을 위한 Amazon EC2 Container Service(ECS) 집중 분석
콘테이너 운영을 위한 Amazon EC2 Container Service(ECS) 집중 분석
 
AWS DevDay 실습 가이드 - 콘테이너
AWS DevDay 실습 가이드 - 콘테이너AWS DevDay 실습 가이드 - 콘테이너
AWS DevDay 실습 가이드 - 콘테이너
 
Tdd 왜 배우기 어려운가
Tdd 왜 배우기 어려운가Tdd 왜 배우기 어려운가
Tdd 왜 배우기 어려운가
 

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

C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기
Heo Seungwook
 
120908 레거시코드활용전략 4장5장
120908 레거시코드활용전략 4장5장120908 레거시코드활용전략 4장5장
120908 레거시코드활용전략 4장5장
tedypicker
 
데이터베이스패턴
데이터베이스패턴데이터베이스패턴
데이터베이스패턴
Suan Lee
 
Effective unit testing ch3. 테스트더블
Effective unit testing   ch3. 테스트더블Effective unit testing   ch3. 테스트더블
Effective unit testing ch3. 테스트더블
YongEun Choi
 
TDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDDTDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDD
Suwon Chae
 
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Ryan Park
 
Agile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And HowAgile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And How
Ryan Park
 

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

자바 테스트 자동화
자바 테스트 자동화자바 테스트 자동화
자바 테스트 자동화
 
Tdd 4장
Tdd 4장Tdd 4장
Tdd 4장
 
C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기C++ 프로젝트에 단위 테스트 도입하기
C++ 프로젝트에 단위 테스트 도입하기
 
10장 결과 검증
10장 결과 검증10장 결과 검증
10장 결과 검증
 
120908 레거시코드활용전략 4장5장
120908 레거시코드활용전략 4장5장120908 레거시코드활용전략 4장5장
120908 레거시코드활용전략 4장5장
 
데이터베이스패턴
데이터베이스패턴데이터베이스패턴
데이터베이스패턴
 
Effective unit testing ch3. 테스트더블
Effective unit testing   ch3. 테스트더블Effective unit testing   ch3. 테스트더블
Effective unit testing ch3. 테스트더블
 
Refactoring - Chapter 8.2
Refactoring - Chapter 8.2Refactoring - Chapter 8.2
Refactoring - Chapter 8.2
 
Spring Boot 2
Spring Boot 2Spring Boot 2
Spring Boot 2
 
Python Unittest
Python UnittestPython Unittest
Python Unittest
 
[2011 04 11]mock_object 소개
[2011 04 11]mock_object 소개[2011 04 11]mock_object 소개
[2011 04 11]mock_object 소개
 
시작하자 단위테스트
시작하자 단위테스트시작하자 단위테스트
시작하자 단위테스트
 
[하코사 세미나] 비전공자의 자바스크립트 도전기
[하코사 세미나] 비전공자의 자바스크립트 도전기 [하코사 세미나] 비전공자의 자바스크립트 도전기
[하코사 세미나] 비전공자의 자바스크립트 도전기
 
08장 객체와 클래스 (기본)
08장 객체와 클래스 (기본)08장 객체와 클래스 (기본)
08장 객체와 클래스 (기본)
 
JUnit & AssertJ
JUnit & AssertJJUnit & AssertJ
JUnit & AssertJ
 
TDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDDTDD&Refactoring Day 03: TDD
TDD&Refactoring Day 03: TDD
 
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...Agd   Test Driven Development For Games What, Why, And How)(Game Connect 2006...
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
 
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
 
Agile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And HowAgile Test Driven Development For Games What, Why, And How
Agile Test Driven Development For Games What, Why, And How
 
비전공자의 자바스크립트 도전기
비전공자의 자바스크립트 도전기비전공자의 자바스크립트 도전기
비전공자의 자바스크립트 도전기
 

Mehr von Suwon Chae (7)

실패한 프로젝트들의 개발문화_개발방법론
실패한 프로젝트들의 개발문화_개발방법론실패한 프로젝트들의 개발문화_개발방법론
실패한 프로젝트들의 개발문화_개발방법론
 
Refactoring
RefactoringRefactoring
Refactoring
 
TDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDTDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDD
 
TDD&Refactoring Day 01: Refactoring
TDD&Refactoring Day 01: RefactoringTDD&Refactoring Day 01: Refactoring
TDD&Refactoring Day 01: Refactoring
 
Ryan Dahl의 Node.js 소개 동영상 해설 by doortts
Ryan Dahl의 Node.js 소개 동영상 해설 by doorttsRyan Dahl의 Node.js 소개 동영상 해설 by doortts
Ryan Dahl의 Node.js 소개 동영상 해설 by doortts
 
Mongo db로 배우는 nosql
Mongo db로 배우는 nosqlMongo db로 배우는 nosql
Mongo db로 배우는 nosql
 
잘하면고효율, 못하면가문의원수가되는 짝프로그래밍 (Effective Pair Programming with Lessons Learned)
잘하면고효율, 못하면가문의원수가되는 짝프로그래밍 (Effective Pair Programming with Lessons Learned)잘하면고효율, 못하면가문의원수가되는 짝프로그래밍 (Effective Pair Programming with Lessons Learned)
잘하면고효율, 못하면가문의원수가되는 짝프로그래밍 (Effective Pair Programming with Lessons Learned)
 

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

  • 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. 보다 더 나은 소프트웨어와 다 웨어와 보다 더 나은 삶을 만들기 위해
  • 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 관점에서 바라봤을 때 드러나는 안 좋은 디자인의 징후 - 단위 테스트 케이스 작성이 어렵다 - 단위 테스트 케이스가 자주 깨진다 깨진다. - 단위 테스트 케이스 실행을 위한 준비해야 할 것이 많다. - 다른 사람의 테스트 케이스를 읽기가 어렵다.
  • 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 굳이 프레임워크를 쓰지 않아도 무방합니다. 업무로직 작성 전에 완성상태를 검증해 줄 수 있는 코드가 존재하기만 하면 충분합니다.
  • 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); }
  • 22. boolean isRight(){ return TDD == UnitTest }
  • 23. (Do) All or Noting
  • 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 ()) } }
  • 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()); // ➑ } }
  • 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