SpringDAO를 이용하면 Hibernate를 이용하는 DAO를 쉽게 만들 수 있다. 핵심은 HibernateTemplate클래스를 이용한 template방식의 접근이며 DataSource DI가 가능하고 template접근이 용이하게 설계된 HibernateDaoSupport를 상속해서 쓰는 것이 일반적이다.

이런 DAO를 테스트 할 때 문제는 DAO라는 것이 그 본질상 Unit Test가 어렵다는 데 있다. 그 이유는 DAO라는 것이 결국 백그라운드에서 돌아가는 persistent 기능을 이용하는 stub과 같은 존재이기 때문에 Unit Test보다는 DB가 참여하는 Integration Test로 작성하는 것이 상식처럼 되어있다. 물론 DB액세스는 데이터준비의 어려움과 테스트 성능저하 등의 부가적인 문제를 가지고 있다. 그래서 Embedded DB갈은 것을 이용한 Fake DB를 사용하는식의 방법을 많이 이용한다. JDBC자체를 Unit Test하기 위한 몇가지 툴이 존재하기는 하는데 사용하기 불편하며 사실 SQL이라는 문장으로 주로 정보가 넘어가기 때문에 그 자체를 비교평가하기가 만만치 않다.

결국 많은 개발자들은 DAO테스트는 integration test로 해야한다는 것을 당연하게 받아들인다.

하지만 static한 query를 날리는 DAO말고 다이나믹한 query를 생성하거나 data logic등을 가지고 있는 DAO라면 Unit Test를 쓰고 싶은 욕심이 생길 수도 있다. Hibernate는 오래된 텍스트 기반의 SQL 또는 그와 유사한 HQL을 사용하지 않고 Criteria라는 OO기반의 Query object를 지원한다. Query의 모든 요소를 object형태로 표현할 수 있기 때문에 dynamic query를 작성하기에 잘 맞으며 OO적이기 때문에 ORM의 구조와도 잘 연결된다. Object로 표현된 모델정보를 다시 HQL같은 텍스트로 표현해 놓으면 당장 리팩토링할때마다 텍스트를 다 찾아서 일일히 수정해줘야 하는 JDBC/Raw SQL을 쓸 때의 문제점에서 크게 벗어날 수 억다. 하지만 Criteria는 그런 면에서 월등하다. 그래서 Hibernate를 좋아하는 개발자들 중에는 그 선호하는 이유가 Criteria때문이라고 얘기하는 사람이 많다. 나도 그런 사람중의 하나이다. 특히 Hibernate 2.x에서는 Criteria가 HQL로 표현할 수 있는 모든 것을 다 표현하지 못해서 불편한 점이 많았는데 3.x에 와서는 거의 대부분의 기능을 다 Criteria를 써서 (심지어 Projection까지) 표현할 수 있기 때문에 더욱 애용되고 있다.

Criteria를 사용하는 코드를 Unit Test한다고 해보자. Criteria는 Session에서 만들어지는데 Unit Test를 하기 위해서라면 그 Session을 mocking하는 것이 필수이다. 하지만 코드에서 Session을 직접가져오는 구조라면 매우 어려울 수 밖에 없다. 하지만 Spring Dao에서는 Template을 이용한 Callback방식을 사용하고 Session을 그 Callback method로 전달해주는 구조로 되어있다. 따라서 이를 잘 활용하면 쉽게 DB와 연동 없이 Unit Test를 만들 수 있다.

간단히 다음과 같은 SpringDao기반의 Hibernate DAO코드가 있다고 치자

[java]
public class DaoImpl extends HibernateDaoSupport {
 public List findEntityByName(final String name) {
  return (List)getHibernateTemplate().execute(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException, SQLException {
    Criteria c = s.createCriteria(Entity.class);
    c.add(Restrictions.eq(“name”, name));
    
    return c.list();
   }});
 }
 …
}
[/java]
findEntityByName이라는 메소드는 내부적으로 anonymous inner class방식으로 HibernateCallback인터페이스를 구현한 callback을 만들고 이를 HibernateTemplate의 execute로 전달한다. 내부에서 이미 시작된 Hibernate session이 있으면 이를 사용하고 없다면 새로운 session을 오픈한 뒤 이를 callback의 파라메터로 전달한다. 이 메소드를 unit test하려면 일단 HibernateTemplate을 먼저 mocking해야 한다. 다행히 HibernateDaoSupport는 HibernateTemplate의 setter를 제공한다. 다만 HibernateTemplate은 인터페이스가 아니고 클래스이므로 proxy를 만들거나 MockClass를 쉽게 만들 수가 없다. 방법은 HibernateTemplate을 상속해서 필요한 메소드를 override하거나 아니면 CGLIB등을 쓰는 방법이 있다. 테스트를 위해 만들어야 하는 메소드는 HibernateTemplate의 execute()이다. 내부적으로 Session을 만들어서 callback의 doInHibernate를 호출해주면 된다. 테스트하고자 하는 핵심은 결국 조건에 따라서 Criteria의 어떤 method를 호출하는 가를 검증하는 것이다. 주로 add(Criterion)메소드에 대해서 검증하는 방법을 쓰면 된다. 그러려면 Criteria를 mock으로 만들어야 한다. 하지만 Criteria는 직접 전달할 수는 없고 파라메터로 넘어가는 Session의 createCriteria를 이용해서 코드 내부에서 생성을 한다. 결국 Session, Criteria두개를 다 mock으로 만들어줘야 한다.

Callback의 내부코드가 실행되는 것을 테스트 해야하기 때문에 좀 복잡해진다. 물론 Callback을 독립적인 클래스로 만들면 별도로 테스트가 가능하겠지만 보통 anonymous inner class를 쓰기 때문에 hibernatetemplate부터 시작해서 3단계의 mock이 필요하다.

내가 만들어 본 것은 두가지 방법을 썼는데 첫째는 직접 클래스와 인터페이스를 이용해서 Mock클래스를 만들어서 쓴 것이다. Manual 방식으로 만들기에 적합한 케이스 중의 하나이다. 이렇게 만들어서 테스트 해본 것은 OSAF의 GenericHibernateDao에 적용을 했었다.

오늘은 이것을 EasyMock을 써서 한번 구현해봤다. EasyMock2.2에는 IAnswer라는 인터페이스를 써서 Mock이 리턴하는 값을 파라메터에 따라서 임의로 조종하게 하는 것이 가능하다. Mock으로 만든 HibernateTemplate의 execute를 실행할때 파라메터로 넘어오는 callback을 그 IAnswer를 구현한 클래스 내에서 직접 호출해주는 코드를 작성하고 그 파라메터로 역시 EasyMock으로 생성한 Session, Criteria의 Mock을 사용하면 된다. 그러면 간단한 Easymock코드를 통해서 callback내부의 코드에 대한 unit test가 가능해진다. 한가지 문제점은 Criteria에 조건으로 넣는 Criterion기반의 SimpleExpression들을 파라메터 비교를 해야하는데 이 바보같은 Hibernate는 SimpleExpression의 비교를 할 수 있는 equals를 구현해 놓지 않았다. 따라서 두개의 Restrictions.add(“a”,”b”)라는 것을 비교하면 다르다고 나와버린다. -_-. 그래서 찾은 방법은 toString을 이용하는 것인데 Hibernate의 scope of object identity개념을 생각하면 entity를 파라메터로 줘도 동일한 클래스이므로 identity비교만으로 두개의 비교가 가능하다. 이를 위해서 파라메터 matching을 위한 간단한 EasyMock 파라메터비교 클래스를 만들었다. SimpleExpression비교는 Hibernate에 이슈로 올려야겠다.

아무튼 그렇게 작성한 코드는 다음과 같다. HibernateTemplate을 mocking하기 위해서 EasyMock class extension을 함께 사용했다.

[java]
package caps.sample.test;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;

import java.util.ArrayList;
import java.util.List;

import org.easymock.IAnswer;
import org.easymock.IArgumentMatcher;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.junit.Before;
import org.junit.Test;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;

public class DaoImplTest {
 DaoImpl daoImpl;
 HibernateTemplate mockHTem;
 
 @Before public void setup() {
  daoImpl = new DaoImpl();
  mockHTem = org.easymock.classextension.EasyMock.createMock(HibernateTemplate.class);
  daoImpl.setHibernateTemplate(mockHTem);
 }
 
 @Test public void testCriteria() {
  org.easymock.classextension.EasyMock.expect(mockHTem.execute((HibernateCallback)anyObject()))
   .andAnswer(new HibernateTemplateExecute());
  
  org.easymock.classextension.EasyMock.replay(mockHTem);
  
  List list = daoImpl.findEntityByName(“Toby”);
  assertEquals(1, list.size());
  
  org.easymock.classextension.EasyMock.verify(mockHTem);
 }
 
 static class HibernateTemplateExecute implements IAnswer {
  public Object answer() throws Throwable {
   HibernateCallback hc = (HibernateCallback)getCurrentArguments()[0];
   Session s = createMock(Session.class);
   Criteria c = createMock(Criteria.class);
   List expected = new ArrayList();
   expected.add(new Entity(“Toby”));
   expect(s.createCriteria(Entity.class)).andReturn(c);
   expect(c.add((Criterion)eqSimpleExpression(Restrictions.eq(“name”,”Toby”)))).andReturn(c);
   expect(c.list()).andReturn(expected);

   replay(s);
   replay(c);
   
   assertSame(expected, hc.doInHibernate(s));
  
   verify(c);
   verify(s);
   
   return expected;
  }
 }
 
 static SimpleExpression eqSimpleExpression(SimpleExpression se) {
  reportMatcher(new SimpleExpressionMatcher(se));
  return null;
 }
 
 static class SimpleExpressionMatcher implements IArgumentMatcher {
  SimpleExpression se;
  SimpleExpression realse;
  
  public SimpleExpressionMatcher(SimpleExpression se) {
   this.se = se;
  }

  public void appendTo(StringBuffer buf) {
   buf.append(“expected ” + se.toString() + ” but ” + realse.toString());
  }

  public boolean matches(Object o) {
   if (!(o instanceof SimpleExpression)) return false;
   realse = (SimpleExpression)o;
   return se.toString().equals(realse.toString());
  }
 }
 
}
[/java]

원하는 테스트는 성공적으로 잘 수행된다. EasyMock의 위력과 편리함을 잘 보여주는 케이스이다.

다만 테스트할 케이스가 많다면 코드가 불필요하게 길어질 것 같다.

그래서 Criteria를 사용하는 Hibernate Callback코드를 쉽게 테스트할 수 있는 Mock클래스를 하나 개발해야겠다는 생각을 하게됐다. Criteria에 조건을 어떤 순서로 넣었는지는 상관없이 파라메터에 따라 몇개의 restriction이 추가되고 모드나 추가 Criteria가 생성이 되었는지 쉽게 체크할 수 있는 그런 Mock을 하나 만들어봐야겠다.

사람들은 이래서 저래서 Unit Test만들기가 어렵다고 한다. 또는 어떤 경우들은 불가능하다고 한다. 하지만 내 경험으로는 Unit Test를 못만들 것은 없다. JUnit Recipe를 보면서 더 강하게 느낀 점이다. Unit Test에 안되는게 어딨니!

update: 그나저나 이 놈의 WordPress는 내 코드의 generic type부분을 태그로 생각해서 다 짤라먹어버렸군 -_-. 제대로 된 code를 표현할 수 있는 블로그로 바꾸고 싶다. ㅜㅜ

Related posts:

  1. spring과 hibernate 함께 쓰기. 그 만만치 않은 작업.
  2. AppFuse DAO Test 코드의 문제점
  3. Hibernate 3.0 released!
  4. Hibernate 3.2 GA 출시
  5. Spring+Hibernate+Eclipse로 .NET을 이기다
  6. Spring보다 많이 팔린 Seam, Wicket
  7. Hibernate In Action
  8. Hibernate 성능 & 최적화
  9. 학습테스트(Learning Test)를 이용해서 공부하기
  10. Spring 3.0 (19) Test 모듈의 선택라이브러리 분석
  11. Spring과 Google Guice의 협력을 통한 DI 애노테이션 표준화 작업 시작
  12. Hibernate vs. SpringFramework Again
  13. Hibernate Tips
  14. 테스트 코드에서 static import를 편하게 넣는 방법
  15. Spring Expressions(SpEL)를 이용한 Mockito Argument Matcher 만들기

Facebook comments:

to “Spring기반의 Hibernate DAO Unit Test 만들기”

  1. Hi my family member! I want to say that this post
    is awesome, great written and include approximately
    all important infos. I’d like to look extra posts like
    this .

  2. My spouse and I stumbled over here by a different website and thought I
    might check things out. I like what I see so now i’m following you.

    Look forward to going over your web page for a second
    time.

  3. Do you mind if I quote a few of your articles as long as I provide credit and sources back to your site?
    My website is in the very same niche as yours and my visitors would genuinely benefit from some of
    the information you present here. Please let me know if this alright
    with you. Cheers!

  4. Having read this I believed it was really informative.

    I appreciate you taking the time and effort to put
    this short article together. I once again find myself spending way too much time both reading and posting comments.
    But so what, it was still worth it!

  5. My family members every time say that I am killing my time here at web,
    except I know I am getting familiarity daily by reading such nice articles or reviews.

  6. Hi there! I could have sworn I’ve visited this web site before but after looking at a few of the posts I realized it’s new to me.

    Anyways, I’m definitely delighted I came across it and I’ll be book-marking it and
    checking back frequently!

  7. I do agree with all of the concepts you’ve introduced on your post.
    They’re really convincing and can definitely work. Nonetheless, the posts are too quick for
    beginners. May just you please prolong them a bit from subsequent time?
    Thank you for the post.

  8. I got this site from my buddy who told me regarding this site
    and at the moment this time I am visiting this site and
    reading very informative articles at this place.

  9. You actually make it seem so easy with your presentation but I find this topic to be really something that I think I
    would never understand. It seems too complex and extremely broad for me.
    I’m looking forward for your next post, I’ll try to get the hang of it!

  10. Just desire to say your article is as astounding. The clearness to your publish is just nice and that i could assume you’re knowledgeable on this subject.
    Well along with your permission let me to grasp your
    feed to stay updated with imminent post. Thank you one million and please keep up the rewarding work.

  11. Great site you have here but I was curious if you knew of any community forums that cover the same topics talked about in this article?
    I’d really like to be a part of group where I can get feedback from other knowledgeable individuals that share the same interest.

    If you have any recommendations, please let me know.
    Kudos!

  12. I am really enjoying the theme/design of your website. Do you
    ever run into any browser compatibility problems?
    A small number of my blog visitors have complained about my website not operating correctly in Explorer but
    looks great in Opera. Do you have any recommendations to help fix
    this issue?

  13. Hello there! Do you know if they make any plugins to safeguard against hackers?
    I’m kinda paranoid about losing everything I’ve worked hard on. Any recommendations?

  14. At this time it sounds like Drupal is the top blogging platform out
    there right now. (from what I’ve read) Is that what you’re using on your blog?

  15. Have you ever considered about including a little bit more than just your articles?
    I mean, what you say is fundamental and all. But imagine
    if you added some great graphics or video clips to give
    your posts more, “pop”! Your content is excellent but with images and clips, this site could undeniably
    be one of the very best in its field. Excellent blog!

  16. whoah this blog is magnificent i really like reading
    your posts. Keep up the good work! You recognize, lots
    of individuals are searching round for this information,
    you can aid them greatly.

  17. Thanks for sharing your thoughts. I truly appreciate your efforts and I am waiting for your next write ups thank you once again.

  18. you’re in reality a just right webmaster. The website loading speed is incredible.
    It seems that you’re doing any distinctive trick.
    Also, The contents are masterpiece. you have done a magnificent task in this topic!

  19. Hello! I know this is kinda off topic however I’d figured I’d ask.
    Would you be interested in exchanging links or maybe guest writing a blog article or vice-versa?
    My site discusses a lot of the same topics as yours and I feel we
    could greatly benefit from each other. If you’re interested
    feel free to shoot me an e-mail. I look forward to hearing from you!
    Awesome blog by the way!

  20. Hi, just wanted to mention, I loved this blog post.

    It was helpful. Keep on posting!

  21. Hey! I know this is somewhat off topic but I was wondering if you
    knew where I could find a captcha plugin for my comment form?
    I’m using the same blog platform as yours and I’m having
    difficulty finding one? Thanks a lot!

  22. I am sure this paragraph has touched all the internet visitors, its really really fastidious
    paragraph on building up new web site.

  23. This blog was… how do I say it? Relevant!!
    Finally I’ve found something that helped me. Cheers!

  24. An interesting discussion is worth comment. I do believe that you should
    publish more about this subject, it might not be a taboo subject but typically people do not talk about these subjects.
    To the next! Kind regards!!

  25. What’s Happening i am new to this, I stumbled upon this I
    have discovered It positively helpful and it has aided me out loads.
    I am hoping to give a contribution & assist different users like
    its aided me. Good job.

  26. Hello, just wanted to mention, I enjoyed this article.
    It was inspiring. Keep on posting!

  27. Thank you, I have recently been looking for info approximately this topic for
    a long time and yours is the greatest I’ve discovered till now.
    But, what concerning the bottom line? Are you positive in regards to the source?

  28. Wow, this post is pleasant, my younger sister is analyzing these kinds of things, therefore I am going to inform her.

  29. I am sure this article has touched all the internet users,
    its really really pleasant article on building up new
    web site.

  30. Hi just wanted to give you a brief heads up and let
    you know a few of the images aren’t loading properly.
    I’m not sure why but I think its a linking issue.

    I’ve tried it in two different browsers and both show the same results.

  31. Informative article, just what I was looking for.

  32. Thanks for one’s marvelous posting! I seriously enjoyed reading it, you can be a great author.I will make sure
    to bookmark your blog and will often come back someday. I want to encourage one to continue your great posts, have a nice holiday
    weekend!

  33. Your style is so unique compared to other folks I’ve read stuff from.
    Many thanks for posting when you have the opportunity, Guess I will just
    book mark this blog.

  34. Pretty nice post. I just stumbled upon your
    blog and wished to say that I’ve truly enjoyed browsing your blog posts.
    After all I’ll be subscribing to your feed and I hope you write again soon!

  35. I don’t even know how I finished up right here, however I assumed this publish was great.
    I don’t recognise who you are however certainly you’re going to a well-known blogger should you aren’t already.
    Cheers!

  36. you’re in point of fact a good webmaster. The website loading pace is incredible.
    It sort of feels that you’re doing any distinctive trick.
    In addition, The contents are masterpiece. you’ve done a excellent task on this subject!

  37. Wonderful post however I was wondering if you could write a litte more on this
    topic? I’d be very grateful if you could elaborate a little bit more.
    Many thanks!

  38. I am really grateful to the owner of this web
    site who has shared this impressive piece of writing at at this place.

  39. Great post! We are linking to this great post on our
    site. Keep up the good writing.

  40. Magnificent beat ! I wish to apprentice while you amend
    your web site, how can i subscribe for a blog
    site? The account aided me a acceptable deal.
    I had been tiny bit acquainted of this your broadcast offered bright clear concept

  41. Great site you have here but I was curious if you knew of any community forums that cover the same topics talked about
    here? I’d really like to be a part of group where I can get feed-back from other knowledgeable
    individuals that share the same interest. If you have any suggestions,
    please let me know. Kudos!

  42. We absolutely love your blog and find nearly all
    of your post’s to be just what I’m looking for. Do you offer guest writers to write content available for you?
    I wouldn’t mind writing a post or elaborating on a lot of the subjects you write with regards
    to here. Again, awesome web site!

  43. Hi friends, its impressive article concerning tutoringand entirely defined,
    keep it up all the time.

  44. Hello my family member! I want to say that this article is
    amazing, nice written and come with approximately all important infos.

    I’d like to see more posts like this .

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha