3. XML 파싱 단위 테스트를 위한 Mock 생성때 큰웃음(?)을 선사하는 DOMElement
스프링도 사용하는 dom4j에 들어있음.. Thanx

    /**
     * Test do parse element bean definition builder.
     */
    public void testDoParseElementBeanDefinitionBuilder() {
       
        MockControl control = MockClassControl.createControl(BeanDefinitionBuilder.class);
        BeanDefinitionBuilder builder = (BeanDefinitionBuilder) control.getMock();
        MockElement mockElement = new MockElement("service");
       
        control.expectAndDefaultReturn(builder.addConstructorArgValue("value of id"), builder);
        control.expectAndDefaultReturn(builder.addConstructorArgValue("value of name"), builder);
        control.expectAndDefaultReturn(builder.addConstructorArgReference("value of ref"), builder);
        control.expectAndDefaultReturn(builder.addConstructorArgValue("value of description"), builder);
        control.expectAndDefaultReturn(builder.addConstructorArgValue("value of available"), builder);
        control.expectAndDefaultReturn(builder.addConstructorArgValue("value of not-available-message-id"), builder);
        control.replay();
       
        serviceBeanDefinitionParser.doParse(mockElement, builder );
        control.verify();
    }
   
    class MockElement extends DOMElement{

        public MockElement(String name) {
            super(name);
            setAttribute("id", "value of id");
            setAttribute("name", "value of name");
            setAttribute("ref", "value of ref");
            setAttribute("description", "value of description");
            setAttribute("available", "value of available");
            setAttribute("not-available-message-id", "value of not-available-message-id");
        }}

2. JUnit
    public void testAssertEqualsOnDoubleHelper() throws Exception {
        new UnitTests().assertEquals("정확하게 같은 값이 아닙니다.", 0.0000D, 0.0000D);
        new UnitTests().assertEquals("정확하게 같은 값이 아닙니다.", 0.0000D, 0.0000f);
        new UnitTests().assertEquals("정확하게 같은 값이 아닙니다.", 1.0000D, 1.0000f);
       
        try{
            new UnitTests().assertEquals(1.000001D, 1.0000f);
            fail("delta가 존재합니다.");
        }catch (AssertionFailedError e) {
            // junit은 사용자가 지정한 오류 메시지 뒤에 대괄호를 붙이고 그 안에 expected와 actual 값을 문자열로 붙인다.
            assertTrue(e.getMessage().startsWith("완벽하게 동일한 값은 아닙니다."));
        }
    }

1. abstract 클래스 테스트하기
    public void testAutowireMode() throws Exception {
        assertEquals("디폴트 Autowire 모드가  AUTOWIRE_BY_NAME이 아닙니다.", AutowiredIntegrationTests.AUTOWIRE_BY_NAME,
                new MyIntegrationTests().getAutowireMode());
    }
   
    class MyIntegrationTests extends AutowiredIntegrationTests{}


이올린에 북마크하기(0) 이올린에 추천하기(0)

테스트가 생활이 되었다면 굳이 예찬할 필요가 없다. 나는 공개적으로 테스트를 예찬해서라도 스스로를 테스트를 피할 수 없는 궁지(?)로 몰아넣고 싶다. ^^

테스트는 코드의 품질을 높여주는 것에만 국한하지는 않는다. Testing Will Challenge Your Conventions라는 제목에 드러나듯 테스트는 테스트를 수행하는 개발자에게 훌륭한 습관을 배양시키기도 한다.

지난 스프링 사용자 모임에서 인터페이스가 반드시 필요하냐는 질문이 있었다. Testing Will Challenge Your Conventions에서 인터페이스가 테스트 전용 객체(Test Double)를 적용하는데 매우 유익함을 설명하고 있다. 구체 클래스를 목킹하는 것을 도와주는 라이브러리가 있기는 하다. 그렇다고 하더라도 인터페이스와 구현체를 분할하여 관점을 나누는 훈련은 파일이 두 배가 되는 번거로움을 충분히 감수할 만큼 개발자에게 유익하다.

Private makes less sense than it used to. 라는 3번 항목이 가장 눈에 띄었다. 실제로 TDD를 연습할 때 로직을 담고 있는 내부의 private 메소드를 테스트하는 것은 매우 어려웠다. 자바 기초를 배울 때 지나치게 강조 되었던 private의 역할에 대해 요즘은 왠지 속았다는 느낌이 든다. 내부의 주요 상태를 포함하는 것이 아니고, 유틸리티 성격의 함수라면 public 메소드로 정의하는 것이 나을지 모른다. 인터페이스에는 포함되지 않으면서 구현 클래스에만 들어 있는 것들은 DI(Dependency Injection) 환경에서는 private 메소드와 유사하게 볼 수도 있다.

하지만, Jaime Metcher의 반론처럼 모든 메소드를 public으로 만들어서 외부(TestCase)에서 테스트하게 할 필요는 없다. public 메소드의 일환으로 테스트 된 코드가 리팩토링 되어 private가 되었다면 다시 테스트 할 필요는 없는 일이다.

Testing Will Challenge Your Conventions에 소개된 12가지 프랙티스는 3번을 제외하고는 널리 알려진 것이다. 생소한 내용이라면 찬찬히 읽어보기를 권한다.

Test Invariant

MartinFowler 2006/09/16 23:28
오랫동안 Design by Contract (DbC) 지지자와 Test Driven Development (TDD) 지지자 사이에서 논쟁이 있어왔다. (역자 주:DbC 는 설계를 먼저해서 요건을 구체화한 Contract 혹은 Interface 중심으로 개발하는 것으로 이해할 수 있구요. TDD 는 테스트로 요건을 정의하는 방식이죠. 즉, 테스트를 성공하면 요건을 만족했다고 보는 방식이죠.) 여기서 그 이야기를 하려는 의도는 없고,  Daniel Jackson 과 얘기했던 착상 즉, 이들 둘을 합쳐보는 것에 대해 언급해보겠다.

Design by Contract 을 따르면(설계를 먼저 하게 되면), 개별 클래스에 대해서 변하지 않는 부분을 정하게 됩니다. 이렇게 정의된 것들은 클래스가 항상 지켜야 할 속성들을 의미합니다. 이들 클래스의 인스턴스인 객체들은 이들 Invariant(혹은 contract)를 항상 충족해야 합니다. (어떤 일을 하는 과정에서 내부적으로 잠시 지키지 않을 수야 있지만)  프로그래밍 언어, Eiffel 을 쓰면 클래스의 invariant 를 메소드 호출 이전(선조건 점검)이나 이후(후조건 점검)에 자동으로 점검할 수 있습니다. Invariant 를 충족하지 않으면 예외를 발생시키죠. (실제 프로그램 사용시점에서는 성능을 고려해서 이러한 기능을 중지시켜 둘 수 있습니다.)

이러한 착상을 TDD 에 적용해보면 클래스의 Invariant 를 테스트하는 공통 메소드를 정의할 수 있습니다. 간단한 예제 코드를 보자.
public class Bowler ...int overs, runs, wickets;

Bowler 클래스의 invariant 는 위 세 개의 변수값이 자연수가 되야 한다는 것이다. 따라서, 다음과 같이 invariant 를 정의할 수 있다.
public boolean passesInvariant() {
return (runs >= 0 && overs >= 0 && wickets >= 0);
}

그리고 나면 테스트 과정에서 셋업 이후와 객체의 활용 이후에 호출할 수 있게 된다.
public void testConcedingRunsAddsToRunsScore() {

  Bowler botham = new Bowler();       // setup - showing my age
  assert botham.passesInvariant();
  botham.concedeRuns(4);              //exercise
  assert botham.passesInvariant();
  assertEquals(4, botham.getRuns());  //verify

}

흥미로운 생각이긴 하지만, 직접 시도해보거나 시도하는 것을 본 것은 아닙니다.
이올린에 북마크하기(0) 이올린에 추천하기(0)