달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2009. 9. 9. 15:48

Mockito Tips TDD2009. 9. 9. 15:48

Stubbing consecutive calls
동일 메소드가 여러번 호출되고, 호출될 때 마다 다른 값을 반환하도록 설정하는 방법

    @Before
    public void before() {
        ...        
        when(articleRepository.findDeletedByOwnerHistoriesFor(targetDate))
            .thenReturn(deletedByOwnerHistoryList)
            .thenReturn(new ArrayList<DeletedByOwnerHistory>());
    }

위의 코드 예에서 articleRepository#findDeletedByOwnerHistoriesFor(targetDate)가 첫번째 호출될 때는 deletedByOwnerHistoryList를 반환하고, 그 이후로 호출될 때는 new ArrayList<DeletedByOwnerHistory>()를 반환한다.
:
Posted by codetemplate
2009. 7. 29. 15:04

Guidelines for Better Unit Tests TDD2009. 7. 29. 15:04

http://www.infoq.com/news/2009/07/Better-Unit-Tests


Jimmy Bogard “Getting value out of your unit tests” 라는 아티클에서 다음과 같은 3가지 규칙을 명시했다.

1. 테스트 이름은 사용자 입장에서 무엇/ 기술해야 한다. - 개발자가 테스트의 이름을 보면 의도된 행위가 무엇인지 이해할 있어야 한다.

2. 테스트도 코드이다. 테스트에도 사랑을 나눠주라. - 운영코드만 리팩토링해야 하는것이 아니다. 읽기 좋은 테스트는 유지보수하기 쉽고, 다른 사람이 이해하기 쉽다. 길고, 복잡한 테스트를 좋아하는 사람은 없다. 테스트에 30 라인이 넘는 셋업 코드가 있다면 해당 코드를 생성 메소드로 분리하라. 테스트 코드는 개발자를 당혹스럽게 한다. 운영코드에 메소드가 있으면 리팩토링하면서 테스트는 코드는 내버려두는가 ?

3. “Don’t settle on one fixture pattern/organizational style” – Sometimes the standard pattern of one class, one test fixture doesn’t work.


Lior Friedman 테스트의 첫번째 원칙은 내부 구조가 아니라 외부적인 행위를 테스트하는 이라고 한다. 달리 표현한다면 해당 클래스에 대한 현재 구조가 아니라 기대치를 테스트하라는 것이다.


Ravichandran Jv 아래와 같은 자신의 규칙을 언급했다.

1. 가능한 하나의 테스트에 하나의 Assert

2. test 메소드 내에 if/else 존재한다면 분기문을 다른 개별 테스트 메소드로 분리하라.

3. 테스트 대상 메소드에 if/else 존재한다면, 메소드도 리팩토링 대상이다.

4. 메소드의 이름은 어떤 테스트인지를 나타내야 한다. ex. TestMakeReservation TestMakeNoReservation 다르다.


NUnit 저자인 Charlie Poole도 하나의 테스트방 하나의 Assert를 언급했다. 


Bryan Cook 아래와 같은 고려할 만한 테스트 관련 목록을 만들었다:


DO: Name Fixtures consistently

DO: Mimic namespaces of Target Code

DO: Name Setup/TearDown methods consistently

CONSIDER: Separating your Tests from your Production Code

DO: Name Tests after Functionality

CONSIDER: Use "Cannot" Prefix for Expected Exceptions


그리고 이상과 같은 모든 이슈들은 Gerard Meszaros “xUnit Test Patterns: Refactoring Test Code”이라는 책에 집대성된다.

:
Posted by codetemplate
2009. 2. 18. 21:44

TDD의 원칙 TDD2009. 2. 18. 21:44

1. 첫번째 법칙: 실패하는 테스트 코드를 작성하기 전에는 프로덕션 코드를 작성하지 않는다.
2. 두번째 법칙: 실해하는 테스트가 수행되는 만큼만 테스트를 작성한다(컴파일될 만큼만).
3. 세번째 법칙: 현재 실패하는 테스트를 성공시키는 만큼만 프로덕션 코드를 작성할 수 있다. 그 이상은 안된다.

이러한 법칙을 따르면 개발자들은 어쩌면 30초 정도의 주기를 갖는 싸이클 작업을 하게 된다.

참 중요한 법칙 하나 더...

Quick & Dirty로 working 코드를 만드는 것이 끝이 아니다.

시간이 없어서 refactoring을 안하고 dirty 코드를 누적시키면 점점 더 시간이 없어질 것이다...

지금 시간이 없더라도 working 코드를 후에 변경/추가가 용이하도록 refactoring 해야 만 후에 시간이 남게 될 것이다.

Refactoring은 TDD/Agile에서 선택이 아닌 필수이다.
:
Posted by codetemplate
:
Posted by codetemplate
2006. 10. 30. 19:53

Mock Testing 개념 TDD2006. 10. 30. 19:53

(http://www.shinetech.com/display/www/What+Are+Mock+Objects%3F)


객체지향 프로그램에서 각 객체는 다른 객체들과 상호작용을 한다.
다음과 같은 그림으로 이를 표현할 수 있다.


Mock Object의 기본 사상은 객체를 unit test할 때 객체가 상호작용하는 collaborator들을 mock collaborator로 변경하는 것이다.


Stub과 다른 점은 아래와 같다.
Mock Objects test themselves
Mock 객체는 테스트 대상 객체에 의해 자신이 적절한 시기에 적절한 방식으로 호출되었는지 조사한다.
Stub은 일반적으로 stubbed data를 단순 반환한다.
Mock Objects are generally extremely lightweight
각 테스트에서 완전히 다른 Mock 인스턴스들이 설정되어야 하는 경우가 있다. Stub은 상대적으로 heavyweight로써 테스트에 재사용된다.
Mock 객체 사용을 위한 디자인 고려 사항은 아래와 같다.
Everything by Interfaces
Dependency Injection
객체의 collaborator는 객체에 전달 되어야 한다. 즉 객체는 자기 자신이 collaborator를 얻지 말아야 한다. 이러한 디자인 기법을 "dependency injection" 또는 "inversion or control"이라고 한다. 이 기법의 자세한 설명은 마틴 파울러의 기사(http://www.martinfowler.com/articles/injection.html)를 참고하기 바란다.

언제 Mock Object를 사용하는 것이 유용한가 ?
Mock의 가장 주요한 잇점은 collaborator를 mock으로 대체함으로써 collaborator 사용을 위해 필요할 수 있는 복잡한 상태 설정이 필요 없게 된다는 점이다.

예를 들어 XML에서 데이터를 가져와서 데이터베이스에 insert하는 비지니스 로직을 고려해 보자. 비지니스 로직을 테스트 할 때 개발자는 XML 파일, 데이터베이스 처리를 위한 복잡한 설정, 데이터베이스 테이블의 내용 조사 등의 작업을 하지 않기를 원할 것이다. 개발자가 원하는 것은 비지니스 로직이 XML 파서, 데이터베이스 계층과 올바로 상호작용하는지를 테스트 하는 것이다. 이러한 경우 개발자는  XML Parser, 데이터베이스 상호작용을 위한 인터페이스를 정의하고 Mock으로 대체함으로써 비지니스 로직만 테스트할 수 있게 된다.

주의할 점은 Mock Object는 unit test를 위한 도구이지 integration test, functional test를 위한 도구가 아니라는 점이다.

:
Posted by codetemplate

http://jwebunit.sourceforge.net

jWebUnit이란 ?

jWebUnit은 웹 어플리케이션을 위한 acceptance test 생성을 위한 편의를 제공하는 자바 프레임워크이다.

jWebUnit은 acceptance test 생성을 위해 HttpUnit, JUnit으로 부터 파생되었다.

jWebUnit이 제공하는 기능 ?

jWebUnit은 웹 어플리케이션 네비게이션, 정합성 확인을 위한 assertion을 등을 포함한 high level API를 제공한다. jWebUnit의 API는 링크를 이용한 네비게이션, 폼 입력 및 서밋, 테이블 내용 validation 및 비지니스 웹 어플리케이션 특징을 포함한다. jWebUnit API는 내부적으로 HttpUnit을 이용한다. jWebUnit은 간단한 네비게이션, 바로 사용 가능한 assertion등을 통해 HttpUnit, JUnit만을 사용했을때 보다 빨리 테스트를 생성할 수 있도록 한다.

아래는 jWebUnit을 이용한 테스트 코드와 HttpUnit, JUnit만을 사용한 테스트 코드이다. 테스트는 HttpUnit 홈 페이지를 구글에서 검색하고, 해당 페이지로 네비게이트하고 사용자 메뉴얼로의 링크가 있는지 조사한다.

먼저 JUnit/HttpUnit Test를 이용한 테스트 코드는 아래와 같다.

public class SearchExample extends TestCase {
  public void testSearch() throws Exception {
     WebConversation wc = new WebConversation();
     WebResponse resp = wc.getResponse( "http://www.google.com");
     WebForm form = resp.getForms()[0];
     form.setParameter("q", "HttpUnit");
     WebRequest req = form.getRequest("btnG");
     resp = wc.getResponse(req);
     assertNotNull(resp.getLinkWith("HttpUnit"));
     resp = resp.getLinkWith("HttpUnit").click();
     assertEquals(resp.getTitle(), "HttpUnit");
     assertNotNull(resp.getLinkWith("User's Manual"));
  }
}

위의 코드를 jWebUnit을 이용하여 구현한 테스트 코드는 아래와 같다.

public class JWebUnitSearchExample extends WebTestCase {
  public JWebUnitSearchExample(String name) {
     super(name);
  }

  public void setUp() {
     getTestContext().setBaseUrl("http://www.google.com");
  }

  public void testSearch() {
     beginAt("/");
     setFormElement("q", "httpunit");
     submit("btnG");
     clickLinkWithText("HttpUnit");
     assertTitleEquals("HttpUnit");
     assertLinkPresentWithText("User's Manual");
  }
}

java script error 무시하기

  protected void setUp() throws Exception {
       super.setUp();

       getTestContext().setBaseUrl("http://oracle.kyobodirect.com:8801");

       HttpUnitOptions.setExceptionsThrownOnScriptError(false);
  }

한글을 이용하여 값을 비교하기


  private String encodeCharSet(String s) {
       try {
           return new String(s.getBytes("EUC_KR"), "8859_1");
       } catch (UnsupportedEncodingException e) {
           e.printStackTrace();
           throw new RuntimeException(e);
       }
  }

  public void testLogIn() {
       beginAt("/login.jsp");

       setFormElement("id", "myid");
       setFormElement("password", "mypassword");

       submit();

       assertTitleEquals(encodeCharSet("환영합니다."));

  }

jWebUnit의 보다 구체적인 사용예를 위해 jsp 파일과 테스트 코드를 첨부한다.

:
Posted by codetemplate