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. 김창준씨 글을 보면 상상력의 빈곤이란 말이 자주 등장하더군요. DAO는 통합테스트 해야해.. 그걸 어떻게 단위테스트해.. 라고 생각했던 제 상상력의 빈곤을 여지없이 깨닫게 하는 포스트군요.. 쩝.

    어노테이션을 적극 채택한 JUnit4는 쉽게 적응할 것 같으네요. 컨셉 변화가 크지 않아 좋아여.. EasyMock에 조금더 적응하면 Mock/Stub을 직접 만들어쓸 일이 거의 없어지겠는데요? 그나저나 빨리 TDD 훈련이 마무리되어야 할터인데.. 나눠서 생각하기. 그게 참 어렵네요. 난 논리의 빈곤인거 같애..

  2. 사람들은 이래서 저래서 Unit Test만들기가 어렵다고 한다. <- 반성하고 갑니다..-ㅅ-

  3. dechirant les parties molles,

  4. 2qBF53 hzsbjfkdgzrj, [url=http://pjbbzosvbibs.com/]pjbbzosvbibs[/url], [link=http://xbghqyrbglji.com/]xbghqyrbglji[/link], http://wjgilpuvzqeo.com/

  5. You should take part in a contest for one of the best websites on the internet. I will recommend this website!

  6. click here to find out more…

    additional info…

  7. bluehost review…

    bluehost review…

  8. You made some decent points there. I seemed on the web for the problem and found most people will associate with along with your website.

  9. jason aldean tickets…

    jason aldean tickets…

  10. nsi warrantech reviews…

    nsi warrantech reviews…

  11. Investigate This Site

  12. CLICK To Investigate

  13. thanks for share!

  14. thank you for share!

  15. thank you for share!

  16. You Could try this out
    [url=http://karagrainger.com/news/fashion.php?p=9194]moncler clothing cheap-They really provide a visual feast for us 2013[/url]
    moncler clothing cheap-They really provide a visual feast for us 2013

  17. is waka flocka and gucci mane beefing Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  18. blue gucci Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  19. Browse Around This Site
    [url=http://australiaukbootsale.com]ugg australia uk[/url]
    ugg australia uk

  20. dillards shoes online Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  21. CLICK To Find Out More
    [url=http://fakebootsonsale.com]fake uggs boots[/url]
    fake uggs boots

  22. shoe sales Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  23. mbt shoes Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  24. womens mbt shoes Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  25. mbt baridi shoes Spring기반의 Hibernate DAO Unit Test 만들기 » Toby’s Epril

  26. Testosterone Cream China
    For a lot of allergic reaction affected individuals, the direction to alleviation can be a bumpy 1, full of success and miss out on concepts that may by no means work, or may well only function for a while of your energy. This is why allergies are really frustrating, they influence your day to day daily life and finding relief is difficult. It’s time to eradicate these allergy symptoms for good, and here are some proven tips that will help.
    Steroid Wholesale
    Mentioned previously formerly in the write-up, there’s usually a potential to suffer a financial damage when confronted with expensive jewelry, whether or not you’re acquiring it, marketing it, cleaning up it, or looking to retail store it. Use the tips you only study in this article to safeguard your jewellery and also to safeguard your budget.Make Decor Effortless With One Of These Tips
    Testosterone Propionate Before And After
    For further fascinating blog articles, consider different concepts every so often and make it exciting. Use distinct formats, try using listings, then add interesting photos. By changing it up now and then, prospective customers won’t become bored experiencing the exact same thing every time they log in. As an alternative, they will be fascinated to view what you are likely to offer up coming!
    Testosterone Enanthate 250

  27. Testosterone Isocaproate Uk
    Odds are, you will have the the occasional setback within your fat loss trip. Usually do not let this help you get lower, or make you stop trying. Instead, start around refreshing with the following dinner, or the very next day. The modifications you happen to be producing usually are not simple and easy will require time.
    Testosterone Canada Price
    It must be explained, that you just aren’t proceeding to become a wines gourmet immediately. However, when you put into practice the recommendations mentioned inside the article previously mentioned, you will end up well on your way to comprehending the dissimilarities involving numerous wine. Eventually, you may be an expert in the area of vino.Great Assistance For Anybody Seeking To Enhance Your Memory
    Equipoise Injection Benefits
    Fixing your credit rating can be quite a very long streets nevertheless one which is definitely worth it. Begin by doing a genuine budget of what you need as opposed to what you desire. Minimize up all charge cards and buy stuff in income or utilizing your bank debit credit card. Then are living within your implies while paying the bills and charge card payments promptly.
    Drostanolone Powder

  28. Injectable Steroids For Psoriasis
    Ensure you possess a emergency first aid kit along. As an alternative, you might load some materials within a plastic case. When with a little luck your trip will probably be clear of accidents, you never know. It is recommended to be prepared to enable you to manage anything that occur in the easiest way probable.
    Equipoise And Winstrol Stack
    Watch your doctor. Well before resigning yourself to hair loss, talk to your primary proper care medical professional. There are numerous situations from thyrois issues to nutritional deficiencies that may be the cause of your hair decrease. In case the hair thinning is caused by an underlying situation, managing the situation is frequently adequate to bring back the growth of hair.
    Masteron Weight Gain
    In case you are in a crash ensure that you demand support at the earliest opportunity. By no means make your supposition that someone different might have referred to as for aid. In the event the accident is pretty minimal you must still need an official come to the scenario to ensure that a car accident report might be sent in.
    Turinabol With Anavar

  29. Dianabol 10 Mg 60 Tablet
    Most condominium buildings make you, the renter, buy this type of water you employ each and every month. Nevertheless, certain areas go with regards to to taking the amount of h2o your whole building utilized in a particular 30 days and splitting it up among the quantity of devices using it. When you use much less drinking water than all the others, it is possible to wind up investing in an individual else’s water usage. Make certain you are conscious of these water consumption rules before signing a lease.
    Testosterone Cypionate How To Use
    When thinking about search engine optimization for any web site, a typical blunder is always to concentration totally on main search engines like google and overlook the relatively less preferred search engine listings. Other search engines like yahoo can command a substantial reveal of search queries, so staying up-to-date with their website guidelines can give you a good edge more than websites specifically customized to just one main online search engine.
    Deca Durabolin Efectos Secundarios En Hombres
    If you have a property alarm system, be sure you’re working with it properly! Your property owner’s insurance will give you a deduction for introducing a security alarm method, but that discounted will go away if somebody will be able to rob your home while the product is into position. Don’t let that eventually you!
    Steroids Injection To Shoulder

  30. Testosterone Enanthate Jelfa Dawkowanie
    1 great trend hint to experience is throwing on the scarf. This really is a great hint for the reason that scarf can practically be regarded the supreme add-on because of the amount of color permutations as well as how simple it is to put 1 on. They are also very easily transportable.
    Winstrol V Side Effects
    Camping out is definitely one of the most amazing and different types of holidays you can find, and you may locate your own property a whole lot more enjoyable if you make sure you are geared up for this! Explore the following article for some extremely valuable information on making your camping trip a total accomplishment!
    Ciclo Winstrol 3 Semanas
    An excellent digital photography strategy which can help you is always to capture a portrait at eye stage to get a perfect chance. Avoid acquiring awesome photos from higher or low perspectives because these pictures can feel strange and impersonal. It’s all a matter of what you would like though.
    Deca Durabolin Lek

  31. Thanks for the marvelous posting! I really enjoyed reading it,
    you can be a great author. I will remember to bookmark your blog and will often come back later in life.
    I want to encourage you to continue your great writing, have a nice weekend!

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