2. 발표자 개
발 자 소개
LG CNS 경영기술교육원 기술교육팀 전임강사
강의 과목
디자인 패턴 & 리팩터링
분석설계 실무
Agile
A il 적용실무
블로그
여름으로 가는 문 blog.doortts.com
Comments
내용이나 후기에 대해서는 Outsider님의 후기
(http://blog.outsider.ne.kr/494) 를 참조 하시면
(htt //bl t id k /494)
좀 더 도움이 될 겁니다.
8. 다음 두 코드 중 더 나은 디자인은?
Case.1 class Rental {
Movie movie;
Rental(Service service) {
this.movie = service.getMovie();
}
}
Case.2
C 2 class R t l {
Rental
Movie movie;
Rental(Movie movie) {
this.movie = movie;
}
}
9. (based on my five years of educational experience)
Strongly recommended approach #1
St l d d h
테스트 주도 개발
Test-Driven D
T t Di Development
l t
10. TDD 관점에서 바라봤을 때 드러나는
안 좋은 디자인의 징후
- 단위 테스트 케이스 작성이 어렵다
- 단위 테스트 케이스가 자주 깨진다
깨진다.
- 단위 테스트 케이스 실행을 위한 준비해야 할 것이 많다.
- 다른 사람의 테스트 케이스를 읽기가 어렵다.
13. “프로그램을 작성하기 전에 테스트 먼저 하라!”
프로그램을 하라!
Test the program before you write it.
“잘 동작하는 깔끔한 코드”
잘
Clean code that works
“질문
질 응답 정
정제 반복
반복”
Ask Respond Refine Repeat
14. p
public class Calculator {
public int sum(int a, int b) {
return 0;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println( calc.sum(10, 20) == 30 );
System.out.println( calc.sum(1, 2) == 3 );
System.out.println( calc.sum(-10, 20) == 10 );
S i l ( l ( 10 )
System.out.println( calc.sum(0, 0) == 0 );
}
} 모두 true면 작성완료!
-----실행 결과-----
실행 결과
false
false
false Comments
true 굳이 프레임워크를 쓰지 않아도 무방합니다. 업무로직 작성 전에 완성상태를
검증해 줄 수 있는 코드가 존재하기만 하면 충분합니다.
16. 생성자 메소드 테스트
(
(constructor method test)
)
public class E l
bli l EmployeeDaoTest {
D T t
@Test
public EmployeeDaoTest {
EmployeeDao d = new E l
E l D dao EmployeeDao();
D ()
assertTrue(dao.isConnected());
}
17. 동치 비교
( q
(equivalence test)
)
@Test
public void testEquals_case2() {
Music musicA = new M i ("BAD" "Mi h l")
i i Music("BAD", "Michael");
Music musicB = new Music("BAD", "Michael");
assertEquals ( musicA musicB);
musicA,
}
18. 동치 비교
( q
(equivalence test)
)
해결책.1
해결책 1 내부 상태(보통은 필드값)를 직접 꺼내와서 각각
비교한다.
해결책.2 toString을 중첩구현해(override)놓고, toString 값
으로 비교한다.
해결책.3 equals 메소드를 중첩구현한다.
해결책.3 Unitils의 assertReflectionEquals를 이용한다.
25. Skeleton vs Incremental
package main;
public class Account {
public Account(int i) {
}
public i
bli int getBalance() {
l ()
return 0;
}
public void withdraw(){
}
public void deposit(){
}
}
26. One method one assert ?
@Test
public void testGetBalance() throws Exception {
bli id l () h i
assertEquals (10000, account.getBalance());
account = new Account(1000);
assertEquals (1000, account.getBalance());
account = new Account(0);
assertEquals (0, account.getBalance());
}
27. Anti-pattern
Anti pattern
private Account account;
@Before
public void setUp(){
account = new Account(10000);
}
@Test
public void testWithdraw() throws Exception {
account.withdraw(1000);
( )
assertEquals(9000, account.getBalance());
}
28. Anti-pattern
public class AccountTest {
@Before
public void setUp(){
}
@Test
p
public void testDeposit() throws Exception {
p () p
Account account = new Account(10000);
account.deposit(1000);
assertEquals(11000, account.getBalance());
}
@Test
public void testWithdraw() throws Exception {
Account account = new Account(10000);
account.withdraw(1000);
assertEquals(9000, account.getBalance());
l ( l ())
}
}
31. 상태기반 테스트
@Test
public void t tFil R
testFileRemove() {
()
FileUtile fileUtil = new FileUtil();
fileUtil.cleanContents( targetFile );
assertEquals( 0 fileUtil size( targetFile ) );
0, fileUtil.size(
}
32. 행위기반 테스트
Case.1
Case 1 입력A
methodA methodB
doSomething rampOn
Case.2 입력B
methodA
methodB
call
doSomething
d S thi rampOn
O
33. 행위기반 테스트
@Test
public void testGetOrderPrice () throws Exception {
PriceCalculator calculator = new PriceCalculator();
Item item = new Item("LightSavor","Kitchen knife",100000);
ICoupon coupon = new Coupon();
assertEquals(93000, calculator.getOrderPrice(item, coupon));
int methodCallCount = ((Coupon)coupon).getIsAppliableCallCount();
assertEquals (1, methodCallCount);
}
34. 행위기반 테스트
public class Coupon implements ICoupon {
private int isAppliableCallCount;
@Override
public boolean isAppliable(Item item) {
isAppliableCallCount++; // 호출되면 증가
출
……..
}
p
public int getIsAppliableCallCount(){
g pp (){
return this.isAppliableCallCount;
}
}
35. 행위기반 테스트
@Test
p
public void testGetOrderPrice () throws Exception {
p
PriceCalculator calculator = new PriceCalculator();
Item item = new Item("LightSavor","Kitchen knife",100000);
Item( LightSavor Kitchen knife 100000);
ICoupon mockCoupon = mock(ICoupon.class);
… // mocking 작업
assertEquals(93000, calculator.getOrderPrice(item, mockCoupon));
verify (
if (mockCoupon, ti
kC times(1)).isAppliable(box);
(1)) i A li bl (b );
}
36. TDD with Spring
스프링 프레임워크의 Unit Test 지원
링 레임워 의
- 의존관계 주입을 통한 객체 생성
- 웹 컨테이너 없는 웹 애플리케이션 테스트
- 단위 테스트 지원 유틸리티
=> Injection과 Mock
37. XmlBeanFacotry로 컨텍스트 가져오는 버전
y 텍
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi http://www.w3.org/2001/XMLSchema instance
xmlns:xsi="http://www w3 org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http //www.springframework.org/schema/beans/spring beans.xsd >
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="music" class="main.MP3">
<constructor-arg value="Belong to me.mp3"/>
g g p
</bean>
<bean id="musicPlayer" class="main.MusicPlayer">
<property name="music" ref="music"/>
</bean>
</beans>
XmlBeanFactory beanFactory
= new XmlBeanFactory(
new ClassPathResource("/context-musicplayer xml")
ClassPathResource( /context musicplayer.xml )
);
38. Unitils를 사용
@RunWith(UnitilsJUnit4TestClassRunner class)
@RunWith(UnitilsJUnit4TestClassRunner.class)
public class UnitilsMusicPlayerTest {
@SpringApplicationContext( /context-musicplayer.xml )
@SpringApplicationContext("/context-musicplayer xml")
private ApplicationContext context;
…
…
}
39. annotation및 autowire를 사용해서
@RunWith(SpringJUnit4ClassRunner class)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/context-musicplayer.xml"})
public class AutowiredMusicPlayerTest {
@Autowired
MusicPlayer player;
…
…
}
40. Injection기능만 사용할 경우
Google Guice(쥬스)로 처리해 보면
static Injector injector = Guice.createInjector(new MusicModule());
@Test
public void testGetFileName() throws Exception {
MusicPlayer player = injector.getInstance(MusicPlayer.class);
assertEquals("Belong To Me.mp3", player.getFileName());
}
41. TDD with Spring – servlet test
이름 박성철
사번 5874
아이디 fupfin
직위 회장
42. TDD with Spring – servlet test
public class EmployeeSearchServletTest {
@Test
public void testSearchByEmpid() throws Exception {
bli id S h id() h i
MockHttpServletRequest request = new MockHttpServletRequest(); // ➊
MockHttpServletResponse response = new MockHttpServletResponse(); // ➋
request.addParameter("empid", "5874") // ➌
ddP (" id" "5874");
EmployeeSearchServlet searchServlet
= new EmployeeSearchServlet(); // ➍
searchServlet.service(request, response); // ➎
Employee employee = (Employee)request getAttribute("employee"); // ➏
(Employee)request.getAttribute( employee );
assertEquals ("박성철", employee.getName() ); // ➐
assertEquals ("5874", employee getEmpid() );
( 5874 employee.getEmpid()
assertEquals ("fupfin", employee.getId() );
assertEquals ("회장", employee.getPosition() );
assertEquals( /SearchResult.jsp ,
assertEquals("/SearchResult.jsp", response.getForwardedUrl()); // ➑
}
}
45. 이미지 참조
Longing for Summer
http://www.flickr.com/photos/68165632@N00/1553091251/
Coupling sketches cropped
http://www.flickr.com/photos/49432745@N03/4533798684/
Tagged, finally...
http://www.flickr.com/photos/kornrawiee/3189034267/
Vincent Boiteau
i i
http://www.flickr.com/photos/2dogs_productions