Whiteship's Note


[테스트] 커스텀 MimeMessageHelper 테스트하기

모하니?/Coding : 2009.08.14 14:07


MimeMessage를 사용해서 실제로 메일을 보내보고, 메일이 깨지지는 않는지 확인해보고 싶어서 다음과 같은 테스트를 작성했습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/testContext.xml")
public class SignupConfirmMimeMessageHelperTest {

    @Autowired
    JavaMailSender mailSender;

    @Test
    public void sendInteface() {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        Member member = new Member();
        member.setEmail("whiteship2000@gmail.com");
        assertNotNull(mimeMessage);
        SignupConfirmMimeMessageHelper helper = new SignupConfirmMimeMessageHelper(mimeMessage);
        helper.makeMessage(member);
        mailSender.send(mimeMessage);
    }

}

테스트를 만들면서 JavaMailSender와 MimeMessage, SignupConfirmMimeMessageHelper 클래스를 어떻게 사용할지 고민을 했죠. 생성자에 member도 같이 줘봤다가.. 뺏다가.. 했습니다. 결국은 빼냈습니다. 중요했던 건 SignupConfirmMimeMessageHelper 클래스의 생성 방법과 사용방법 이었습니다.

저런식으로 실제 메일을 보내 본 뒤.. 그대로 두면.. 테스트가 돌 때 마다 저한테 메일을 보내줄 겁니다. 상당히 귀찮은 테스트입니다. 그래서 생각을 했습니다. 안 되겠다. 어차피 메일 보내는 것도 확인했고.. 인코딩도 확인했고.. 내가 이 테스트에서 정하고자 했던 건 SignupConfirmMimeMessageHelper 클래스의 생성자 구조랑 사용법이니깐... 단위테스트로 고치자~

@RunWith(MockitoJUnitRunner.class)
public class SignupConfirmMimeMessageHelperTest {

    @Mock JavaMailSender mockSender;
    @Mock MimeMessage mockMessage;

    @Test
    public void sendInteface() {
        assertNotNull(mockSender);
        assertNotNull(mockMessage);
        when(mockSender.createMimeMessage()).thenReturn(mockMessage);
       
        MimeMessage mimeMessage = mockSender.createMimeMessage();
        Member member = new Member();
        member.setEmail("whiteship2000@gmail.com");
        SignupConfirmMimeMessageHelper helper =
            new SignupConfirmMimeMessageHelper(mimeMessage);
        helper.makeMessage(member);
        mockSender.send(mimeMessage);
    }

}

그래서 이렇게 고쳤습니다. 고치고 나니까... 이런 생각이 드네요.


맨 마지막 줄만 지워버릴껄 그랬나.... 허허헐...
top

Write a comment.


Spring MVC 리팩토링 2

모하니?/Coding : 2007.06.22 13:04


이전 글에서 Controller 단위 테스트를 변경하여 Controller 수정에 성공했습니다.
이번에는 MemberService를 단위 테스트 해서 MemberService의 구현을 수정하겠습니다.

이번에는 MemberService의 테스트가 만들어져 있지 않은 것을 확인했습니다. 만들어야겠습니다.

public class MemberServiceImplTest {

    private MemberService memberService;
    private MemberDao memberDao;

    @Before
    public void setUp() {
        memberService = new MemberServiceImpl();
        memberDao = createMock(MemberDao.class);
        memberService.setMemberDao(memberDao);
    }

    @Test
    public void testNotJoinedMail() {
        String mail = "";
        expect(memberDao.findByMail(mail)).andReturn(null);
        replay(memberDao);
        Member member = memberService.isJoined(mail);
        assertEquals(null, member);
        verify(memberDao);
    }
}

일단 Bad Case를 테스트 하는 코드를 작성했습니다.

사용자 삽입 이미지
이전 글에서 컨트롤러를 테스트 할 때 인터페이스 만들고 구현체에서 에러 나길래 퀵픽스로 구현해둔 곳에서 에러가 발생했습니다.

이 방법은 KSUG 2회 세미나에서 영회형이 발표하실 때 언급하셨고 J2EE Development without EJB에서 Rod Johnson이 추천하는 방법이기도 합니다.

어쨋든 테스트가 돌아가도록 ServiceImple 구현을 변경합니다.

이런 여기서도 또하나 결정을 해야겠군요. Service 계층에서 사용할 DAO의 인터페이스를 결정해야 합니다. 이전에는 findByMail을 사용하고 있었는데 썩 나쁘지 않은 것 같습니다. 따라서 그대로 사용을 하고 대신 리턴 타입을 List<Member> 로 할지 Member로 할지 고민이 됩니다. 같은 mail을 가진 데이터가 들어갈 수 없다는 제약 사항이 있다면 리턴 타입을 Member로 해야하고... 혹시라도 그런 제약 사항이 없다면 List<Member>로 받아서 뭔가 복잡한 처리가 필요하면 View까지 그 영향이 갈 것 같네요;;;

이번에도 저는 혼자 개발하고 있기 때문에 다음과 같은 장점이 있습니다.

"니가 곧 개발자며 사용자이자 설계자다. 니가 다 알아서 해라! 그리고 심심하면 DBA도 해라."

OK 저는 같은 mail을 가진 Member 데이터가 데이터베이스에 들어가지 않기로 정했습니다. 이 제약 사항은 다음에 DAO를 테스트할 때 확인 해야 겠습니다.

    public Member isJoined(String mail) {
        return memberDao.findByMail(mail);
    }

Service 구현은 여전히 변함없이 간단합니다. 이 순간 저는 지금 상당히 많이 돌아가고 있다는 것을 느낍니다. 단지 메소드 이름 하나 변경하는 일이였는데 너무 돌아가는 거 아닌가 싶지만 목적은 구현보다는 테스트에 익숙해지는 것이기 때문에 계속해서 진행하겠습니다.

이번에는 새로운 테스트를 작성하여 해당 mail을 가지고 있는 Member를 검색했을 경우를 테스트 합니다.

    @Test
    public void testJoinedMail() {
        String mail = "keesun@mail.com";
        Member member = new Member();
        member.setMail(mail);
        expect(memberDao.findByMail(mail)).andReturn(member);
        replay(memberDao);
        assertEquals(member, memberService.isJoined(mail));
        verify(memberDao);
    }

테스트는 통과합니다.

사용자 삽입 이미지

'모하니? > Coding' 카테고리의 다른 글

SimpleFormAndMultiActionController 개발 구상  (0) 2007.07.17
ServletRequestUtils 사용하기  (6) 2007.07.10
DB 바꾸기(Hibernate 사용할 때)  (0) 2007.06.27
Spring MVC 리팩토링 4  (0) 2007.06.22
Spring MVC 리팩토링 3  (0) 2007.06.22
Spring MVC 리팩토링 2  (0) 2007.06.22
Spring MVC 리팩토링 1  (0) 2007.06.22
Seminar Helper v0.8  (0) 2007.06.20
어떤 계층 순서대로 구현 하시나요?  (0) 2007.06.18
Clover 설치하기  (0) 2007.06.18
16강 토너먼트 알고리즘  (0) 2007.06.12
top

Write a comment.


Unit Tests



참조 : Spring MVC 10장

단위 테스트란?
The basic definition of a unit test is “a discrete test condition to check correctness of an isolated software module.”
진짜 단위 테스트라고 하려면 다음과 같은 특징을 가져야 합니다.
1. 빠르게 실행되어야 합니다.
- DB 커넥션이나 외부 자원등을 읽느라 테스트가 느려지면 해당 테스트의 유용성을 급격히 떨어집니다. 즉각적인 응답을 받을 수 있어야 합니다.

2. 외부 설정이 필요없습니다.
- 테스트 코드를 빠르게 실행을 하고 외부와 종속성을 제거하기 위함입니다.

3. 다른 테스트들과 독립적이어야 합니다.
- 완전히 독립적인 테스트여야 합니다. 다른 테스트를 먼저 또는 나중에 실행해야 하는 관계는 없어야 합니다.

4. 외부 자원이 필요없습니다.
- DB 커넥션이나 웹서비스에 종속성을 가지면 안됩니다.

5. 흔적을 남기지 않습니다.
- 단위 테스트를 계속해서 반복적으로 수행하기 위해서는 흔적이 남으면 안됩니다. 외부 자원에 종속되지 않는 것이 도움이 됩니다.

6. 가능한한 작은 단위로 테스트 합니다.
- OO에서는 보통 메소드 단위로 작성 합니다.

진정한 단위 테스트라면 Spring의 ApplicationContext나 BeanFactory에서 bean을 가져와서 테스트를 하면 안 되는 거군요. 외부 자원과 설정에 의존하게 되는 거니깐 그냥 TestCase만 가져다가 테스트를 만들면 되겠습니다. 다른 테스트들에 독립적인 테스트 메소드들을 구현해야겠습니다.


top

Write a comment.


Easymock 연습하기 3탄



Easymock 연습하기 2탄에서는 mock 객체를 이용해서 void 형태의 메소드를 테스트 했습니다. 즉 올바른 인자가 들어가는지 확인을 한것입니다.

이번에는 나오는 값도 테스트를 해보겠습니다. 나오는 값을 테스트하기 위해서는 expect라는 메소드와 andReturn메소드를 사용합니다.

소스보기

expect를 이용해서 리턴값이 있는 메소드를 감싸고 그 뒤에 andReturn을 호출하면서 반활 될 타입의 객체를 지정해 줍니다. => "memberDao에 get메소드를 호출하면 member가 나온다."

그리고 replay를 실행하여 실제로 member를 추가한 뒤에 memberRepository를 이용해서 get을 호출하면 memberRepository 안에 있는 memberDao의 Mock 객체가 member를 반환해주게 됩니다. 따라서 시나리오와 액션이 일치하게 되며 verify를 이용해서 확인할 수 있습니다.


위 메소드의 내용을 한번 그려봤습니다. Dao 쪽은 Mock으로 처리하고 Service만 test를 할 수 있게 되었습니다.

'Spring > 주소록 만들기' 카테고리의 다른 글

중간점검  (0) 2006.12.17
HTML 공부 중 2탄  (2) 2006.12.14
Strict HTML 4.01 지침서  (6) 2006.12.14
HTML 공부 중  (2) 2006.12.13
페이징 기능 구현하기(TDD, Easymock, iBATIS, MySQL)  (2) 2006.12.11
Easymock 연습하기 3탄  (0) 2006.12.06
Easymock 연습하기 2탄  (0) 2006.12.05
Easymock 연습하기  (0) 2006.12.05
단위 테스트 모르겠슴 ㅠ.ㅠ  (0) 2006.12.05
Eclipse에서 Rename Method 리팩토링  (0) 2006.12.04
세련된 SQL map (iBATIS)  (0) 2006.12.04
top

Write a comment.


Easymock 연습하기



http://www.easymock.org/Downloads.html
위 링크에서 Easymock을 다운로드 합니다.

압축을 풀면 easymock.jar 파일이 있습니다. 이것을 Eclipse 프로젝트에 추가합니다. 그리고 JUnit 도 추가합니다. JDK는 5.0 이상에서 작동합니다. (easymock 버젼 2 부터 그런 것 같습니다.)

MemberRepositoryImpl 클래스를 테스트 하기 위한 코드 입니다.
import static org.easymock.EasyMock.*;
import org.junit.*;
import net.webapp2.member.dao.MemberDao;

public class MemberRepositoryTest {

   MemberDao mockMemberDao;
   MemberRepository memberRepository;

   @Before
   public void setup(){
       mockMemberDao = createMock(MemberDao.class);
       memberRepository = new MemberRepositoryImpl();
   }

   @Test
   public void add(){
       //recoding

       //verify
   }
}

이 전에는 MemberDao 까지 거쳐서 테스트를 했었지만 위의 상태는 아래의 그림과 같이 MemberRepository에만 국한 될 수 있습니다.

Easymock을 사용하는 방법은 세 단계로 나뉘어 집니다. 먼저 createMock(Interface Type)을 사용하여 Mock 객체를 만들게 됩니다. 인터페이스 타입의 객체는 못만들기 때문에 MemberDao를 사용하고 있던 MemberRepositoryImpl에서 SqlmapMemberDao(MemberDao 구현체) 까지 사용해가며 테스트를 했었는데 이제는 거기까지 가지 않고 MemberDao에서 끊기게 됩니다.

그 다음 어떤 활동이 벌어질지 시나리오를 작성하고(recoding) 실제 작업이 돌아갈 때 결과가 시나리오와 일치 하는지 확인(verify)합니다.

참고 :
Easymock Document
Easymock 시작하기
단위 테스트의 '단위'
단위 테스트의 경계 : 어디까지가 단위 테스트인가?

'Spring > 주소록 만들기' 카테고리의 다른 글

Strict HTML 4.01 지침서  (6) 2006.12.14
HTML 공부 중  (2) 2006.12.13
페이징 기능 구현하기(TDD, Easymock, iBATIS, MySQL)  (2) 2006.12.11
Easymock 연습하기 3탄  (0) 2006.12.06
Easymock 연습하기 2탄  (0) 2006.12.05
Easymock 연습하기  (0) 2006.12.05
단위 테스트 모르겠슴 ㅠ.ㅠ  (0) 2006.12.05
Eclipse에서 Rename Method 리팩토링  (0) 2006.12.04
세련된 SQL map (iBATIS)  (0) 2006.12.04
메소드 추상화  (2) 2006.12.04
log4j.properties 파일 위치 시키기  (2) 2006.12.04
top

Write a comment.