CreateManager_ko |
|
Your trail: |
Part II: 새로운 Managers 작성하기 - 데이타베이스 계층(DAOs)과 통신하며 트랜잭션을 관리하는 Business Facade 들을 작성하는 방법
- 이 tutorial 은 Part I: AppFuse에서 새로운 DAO들과 Object들을 작성하기 에 의존 되어 있습니다.
About this Tutorial
이 튜토리얼은 Part I 에서 작성한 DAO 와 연결하기 위한 Business Facade 클래스와 이를 테스트할 JUnit Test 를 작성하는 방법을 알려줍니다.
AppFuse에서는 이런(Business Facade) 클래스를 Manager class 라고 부릅니다.
이 Manager 클래스의 주된 임무는 persistence (DAO) layer 와 web layer 를 연결하는 것입니다.
이것은 프리젠테이션 계층과 데이타베이스 계층의 커플링을 줄이는데도 유용합니다.
또한 Manager 는 당신의 어플리케이션 비즈니스 로직을 넣는 곳이기도 합니다.
그럼 ManagerTest 와 Manager 을 생성해 봅시다.
Table of Contents
- Manager 클래스를 JUnit으로 테스트 하기 위한 ManagerTest 를 만듭니다
- DAO 와 통신하기 위한 Manager 를 만듭니다
- Manager 클래스의 그 Transaction 처리를 위한 Spring 프레임워크를 구성합니다.
- ManagerTest 를 실행합니다
Manager 클래스를 JUnit으로 테스트 하기 위한 ManagerTest 를 만듭니다
Part I에서 우리는 Person object 와 PersonDao 를 만들었습니다 - 그럼 계속해서 진행해 봅시다.
첫째로, PersonManager 을 위한 JUnit test 를 만들어 봅니다.
test/service/**/service 에 PersonManagerTest 를 만듭니다.
이것은 DAO 가 가지고 있는 메소드(get, save, remove) 와 똑같은 메소드들을 테스트 할 것입니다.
- 매우 중복되어 보이지만(왜 모든걸 테스트 하는거야!), 6개월이 지난 후에는 이러한 테스트가 좋다는 것을 알게 될 것입니다.
이 클래스는 service 패키지에 들어있는 BaseManagerTestCase 을 반드시 상속받아야 합니다.
이 부모 클래스(BaseManagerTestCase)는 BaseDaoTestCase 와 비슷한 기능을 제공합니다.
The code below is what you need for a basic JUnit test of your Manager. Unlike the DaoTest, this test uses jMock to isolate the Manager from its dependencies and make it a true "unit" test. This can be very helpful because it allows you to test your business logic w/o worrying about other dependencies. The code below simply sets up the Manger and its dependencies (as Mocks) for testing.
package org.appfuse.service;
import java.util.List;
import java.util.ArrayList;
import org.appfuse.dao.PersonDao;
import org.appfuse.model.Person;
import org.appfuse.service.impl.PersonManagerImpl;
import org.jmock.Mock;
import org.springframework.orm.ObjectRetrievalFailureException;
public class PersonManagerTest extends BaseManagerTestCase {
private final String personId = "1";
private PersonManager personManager = new PersonManagerImpl();
private Mock personDao = null;
private Person person = null;
protected void setUp() throws Exception {
super.setUp();
personDao = new Mock(PersonDao.class);
personManager.setPersonDao((PersonDao) personDao.proxy());
}
protected void tearDown() throws Exception {
super.tearDown();
personManager = null;
}
}
|
이것으로 뼈대는 완성되었으므로 이제 살만 붙이면 됩니다.
그럼 모든것이 다 잘 작동하는지 확인하는 test method 들을 추가해 봅시다.
여기 우리가 test method 를 작성하는데에 도움이 될만한 글을 DAO Tutorial으로 부터 발췌해서 적어봤습니다.
- 이 메소드가 Ant build.xml 파일의 <junit> 태스크에 의해 불려 질려면 몇가지 규칙이 필요합니다.
- - 메소드 이름이 test(모두 소문자)로 시작해야 한다
- - public 메소드여야 한다.
- - 리턴타입이 void 이여야 한다.
- - 전달 인자가 없어야 한다.
- 여기 CRUD 테스트를 위한 간단한 테스트 코드가 있습니다.
중요한 것은 각각의 메소드들은 자발적으로 일어난다는 점 입니다.
다음의 코드를 PersonManagerTest.java 에 추가 합니다:
public void testGetPerson() throws Exception {
// set expected behavior on dao
personDao.expects(once()).method("getPerson")
.will(returnValue(new Person()));
person = personManager.getPerson(personId);
assertTrue(person != null);
personDao.verify();
}
public void testSavePerson() throws Exception {
// set expected behavior on dao
personDao.expects(once()).method("savePerson")
.with(same(person)).isVoid();
personManager.savePerson(person);
personDao.verify();
}
public void testAddAndRemovePerson() throws Exception {
person = new Person();
// set required fields
person.setFirstName("firstName");
person.setLastName("lastName");
// set expected behavior on dao
personDao.expects(once()).method("savePerson")
.with(same(person)).isVoid();
personManager.savePerson(person);
personDao.verify();
// reset expectations
personDao.reset();
personDao.expects(once()).method("removePerson").with(eq(new Long(personId)));
personManager.removePerson(personId);
personDao.verify();
// reset expectations
personDao.reset();
// remove
Exception ex = new ObjectRetrievalFailureException(Person.class, person.getId());
personDao.expects(once()).method("removePerson").isVoid();
personDao.expects(once()).method("getPerson").will(throwException(ex));
personManager.removePerson(personId);
try {
personManager.getPerson(personId);
fail("Person with identifier '" + personId + "' found in database");
} catch (ObjectRetrievalFailureException e) {
assertNotNull(e.getMessage());
}
personDao.verify();
}
|
PersonManager 인터페이스를 아직 작성하지 않았기 때문에 이 class 는 컴파일 되지 않을 겁니다.
- I think it's funny how I've followed so many patterns to allow extendibility in AppFuse. In reality, on most projects I've been on - I learn so much in a year that I don't want to extend the architecture - I want to rewrite it. Hopefully by keeping AppFuse up to date with my perceived best practices, this won't happen as much. Each year will just be an upgrade to the latest AppFuse, rather than a re-write. ;-)
DAO 와의 대화를 위한 Manager 를 만듭니다
첫째로, src/service/**/service 디렉토리에 PersonManager.java interface 를 만듭니다. 그리고 기본적인 CRUD 메소드를 정의합니다.
setPersonDao 메소드는 PersonManagerTest 에서 DAO 를 세팅하기 위해서 만들어진 메소드로서 다른 곳에서는 거의 사용될 일이 없습니다.
요점만 보여주기 위하여 아래 클래스에 JavaDoc 들을 삭제했습니다.
package org.appfuse.service;
import org.appfuse.model.Person;
import org.appfuse.dao.PersonDao;
public interface PersonManager {
public void setPersonDao(PersonDao dao);
public Person getPerson(String id);
public void savePerson(Person person);
public void removePerson(String id);
}
|
자 이제 PersonManager 의 메소드들을 구현할 PersonManagerImpl 을 만들어 봅시다.
우선 src/service/**/service/impl 에 PersonManagerImpl.java 파일을 만듭시다.
그리고 이 클래스는 BaseManager 를 상속(extend)받고 PersonManager 를 구현(implement)하도록 합니다.
package org.appfuse.service.impl;
import org.appfuse.model.Person;
import org.appfuse.dao.PersonDao;
import org.appfuse.service.PersonManager;
public class PersonManagerImpl extends BaseManager implements PersonManager {
private PersonDao dao;
public void setPersonDao(PersonDao dao) {
this.dao = dao;
}
public Person getPerson(String id) {
return dao.getPerson(Long.valueOf(id));
}
public void savePerson(Person person) {
dao.savePerson(person);
}
public void removePerson(String id) {
dao.removePerson(Long.valueOf(id));
}
}
|
setPersonDao 메소드는 PersonDao 와 이 Manager 를 연결하기 위하여 Spring 프레임워크에 의해 호출됩니다.
위 연결은 Step 3에서 applicationContext-service.xml 파일에서 설정할 것입니다.
이제 "ant compile-service" 로 컴파일 할수 있습니다.
그럼 이제 services layer 을 위한 Spring 설정파일을 수정해 봅시다.
Manager 클래스의 그 Transaction 처리를 위한 Spring 프레임워크를 구성합니다.
PersonManager 와 PersonManagerImpl 을 Spring 프레임워크에 알리기 위해 src/service/**/service/applicationContext-service.xml 파일을 열어서 다음과 같은 라인을 추가 합니다.
(personManager 을 위한 정의가 주석처리 되어있을 겁니다. 다음 라인을 추가 하지 않고 그 주석을 해제해도 됩니다.)
<bean id="personManager" parent="txProxyTemplate">
<property name="target">
<bean class="org.appfuse.service.impl.PersonManagerImpl" autowire="byName"/>
</property>
</bean>
|
"parent" 속성에 참조된 TransactionProxyFactoryBean bean 정의는 모든 기본적인 트랜잭션 속성을 가지고 있습니다.
ManagerTest 를 실행합니다.
모든 파일을 저장하고 "ant test-service -Dtestcase=PersonManager" 태스크를 실행해 보세요.
Yeah Baby, Yeah:
BUILD SUCCESSFUL
Total time: 9 seconds
여기까지 우리가 수정하고 추가한 파일들은 여기서 다운 받을 수 있습니다.
Next Up: Part III: Actions 클래스 들과 JSP 작성하기 - Action 클래스들과 JSP 페이지들을 만드는 방법.
|