10. JUnit
프로그래머 중심의 자바용 테스트 프레임워크. 단위 테스트
를 위한 xUnit 아키텍처의 한 종류
xUnit : http://en.wikipedia.org/wiki/XUnit
JUnit : http://junit.org
JUnit 설계 목표
유용한 테스트를 작성하는데 도움이 되어야 함
시간이 지나도 가치 있는 테스트를 작성하는데 도움이 되어야 함
코드를 재사용함으로써 테스트 작성 비용을 낮추는데 도움이 되
어야 함
11. JUnit
JUnit is an open source framework which is used for writing & running tests.
Provides Assertions for testing expected results.
Provides Test runners for running tests.
JUnit tests allow you to write code faster which increasing quality
JUnit is elegantly simple. It is less complex & takes less time.
JUnit tests can be run automatically and they check their own results and
provide immediate feedback.
JUnit tests can be organized into test suites containing test cases and even
other test suites.
Junit shows test progress in a bar that is green if test is going fine and it
turns red when a test fails.
16.
(test fixture)
테스트의 기반이 되는 일관된 실행 환경
테스트 수행 전에 테스트 설비를 설치(setup) 작업
과 테스트 수행 후에 초기 상태로 설비를 해체
(tearDown)하는 작업 필요
@Before, @After
@BeforeClass, @AfterClass (정적 메서드)
19.
(hamcrest)
복잡한 단언문을 서술적으로 가독성 높게 작성 가능
실패 시 문제 파악에 도움이 되는 정보 제공
http://hamcrest.org/
JUnit 4 , assertThat(T actual, Matcher<T> matcher)
핵심 매처: org.hamcrest.CoreMatchers
전체 매처: org.hamcrest.Matchers
assertTrue(result.contains(“x”) || result.contains(“y”) || result.contains(“z”));
assertThat(result, is(hasItem(anyOf(equalTo(“x”, equalTo(“y”), equalTo(“z”)))));
20. import static org.hamcrest.CoreMatchers.*;
논리
allOf(), anyOf():
not(): 비교 결과 부정 또는 다름
컬렉션(Collection)
hasItem(), hasItems():
문자열
contansString(), containsStringIgnoringCase():
startWith(), startsWithIgnoringCase():
endsWith(), endsWithIgnoringCase():
21. 기본
equalTo(): 두 객체가 동일한지 판단
is(Matcher matcher): 단언문의 서술성을 높임, 기능 없음
is(Object o): equalTo()와 동일
anything(): 항상 참
객체
any(), instanceOf(): 특정 유형의 클래스면 참
isA(): is(instanceOf(…))
notNullValue(), nullValue(): 널 확인
sameInstance(): 동일 객체 인스턴스 확인
27. 제라드 메스자로스(Gerad Meszaros)가 xUnit
Test Patterns 책을 통해 소개한 용어
영화에서 위험한 장면에서 배우 대신 등장하는 스
턴트 대역(Stunt Double)을 차용
특정 객체를 사용해서 테스트를 진행하기 어려울
경우, 그 객체 대신 사용하는 테스트용 객체
훨씬 협조적이고 원하는 대로 테스트를 작성할 수
있게 해주는 대역 객체
28. 테스트 대상을 의존 객체와 관계를 끊고 격리시킬 때
느린 테스트를 더 빠르게 만들어 자주 실행하고 싶을 때
예측 불가능한 요소를 통제하여 테스트하고 싶을 때
특수한 상황을 시뮬레이션하고 싶을 때
감춰진 정보를 얻어내는 용도로 활용하고 싶을 때
통합 환경 부재로 실제와 같은 대용품 객체를 만들고 싶을 때
필요한 객체를 점진적으로 설계하는 방식(Need-Driven
Development)으로 테스트 및 구현을 확장해 나가고 싶을 때
30.
(Dummy Object)
단순한 빈 껍데기 객체가 필요할 때 사용
테스트 대상 객체의 생성자나 메서드의 인자로 전
달하는 용도
아무런 기능도 구현되지 않으며 호출됐을 때 정상
동작은 보장되지 않음
일부에선 호출을 가정해서 만들었다고 보기 힘들기
때문에 만일 호출시엔 예외를 발생시켜야 한다고
말함 (http://toby.epril.com/?p=706)
31. (Test Stub)
필요한 만큼 최소한의 기능만 구현
주로 정해진 값을 반환하도록 하드 코딩
public class LoggerStub implements Logger {
public void log(LogLevel level, String message) {
throw new UnsupportedOperationException();
}
public LogLevel getLogLevel() {
return LogLevel.WARN;
}
}
32. (Test Spy)
사용 여부, 호출 내용, 처리 결과 등을 기록 했다가 나
중에 확인 가능
public class LogTargetSpy implements LogTarget {
public void write(Level level, String message) {
log.add(level + “:” + message);
}
boolean received(Level level, String message) {
return log.contains(level + “:” + message);
}
}
33. (Fake Object)
원 객체보다는 단순하지만 여러 테스트에서 공용으로 사용할 수
준으로 구현된 객체
테스트 토막을 테스트 시나리오 별로 따로 만드는 번거로움을 해
결
실제 객체의 대용품(ex 실 DB 대신 메모리에 저장하는 DAO)
34. (Mock Object)
특정 조건에 정해진 행위를 취함
상태보다는 행위 기반으로 테스트를 작성
모의 객체 제작용 프레임워크를 주로 활용해서 제작
모의객체는 스텁이 아니다(Mocks Aren't Stubs)
http://testing.jabberstory.net/
42. 1/3
any() Matches anything, including nulls
any(Class<T> clazz) Matches any object, including nulls
anyBoolean() Any boolean or non-null Boolean
anyByte() Any byte or non-null Byte.
anyChar() Any char or non-null Character.
anyCollection() Any non-null Collection.
anyCollectionOf(Class<T> clazz) Generic friendly alias to anyCollection().
anyDouble() Any double or non-null Double.
anyFloat() Any float or non-null Float.
anyInt() Any int or non-null Integer.
anyList() Any non-null List.
anyListOf(Class<T> clazz) Generic friendly alias to anyList().
anyLong() Any long or non-null Long.
anyMap() Any non-null Map.
anyMapOf(Class<K> keyClazz, Class<V> valueClazz) Generic friendly alias to
anyMap().
anyObject() Matches anything, including null.
anySet() Any non-null Set.
anySetOf(Class<T> clazz) Generic friendly alias to anySet().
anyShort() Any short or non-null
anyString() Any non-null String
anyVararg() Any vararg, meaning any number and values of arguments.
43. 2/3
Hamcrest
argThat(org.hamcrest.Matcher<T> matcher)
booleanThat(org.hamcrest.Matcher<Boolean> matcher)
byteThat(org.hamcrest.Matcher<Byte> matcher)
charThat(org.hamcrest.Matcher<Char> matcher)
doubleThat(org.hamcrest.Matcher<Double> matcher)
floatThat(org.hamcrest.Matcher<Float> matcher)
intThat(org.hamcrest.Matcher<Integer> matcher)
longThat(org.hamcrest.Matcher<Long> matcher)
shortThat(org.hamcrest.Matcher<Short> matcher)
contains(String substring) String argument that contains the given
substring.
matchers.
endsWith(String suffix) String argument that ends with the given suffix.
matches(String regex) String argument that matches the given regular
expression.
refEq(T value, String... excludeFields) Object argument that is reflection-
equal to the given value with support for excluding selected fields from a
class.
startsWith(String prefix) String argument that starts with the given
prefix.
44. 3/3
eq(boolean value) boolean argument that is equal to the given value.
eq(byte value) byte argument that is equal to the given value.
eq(char value) char argument that is equal to the given value.
eq(double value) double argument that is equal to the given value.
eq(float value) float argument that is equal to the given value.
eq(int value) int argument that is equal to the given value.
eq(long value) long argument that is equal to the given value.
eq(short value) short argument that is equal to the given value.
eq(T value) Object argument that is equal to the given value.
isA(Class<T> clazz) Object argument that implements the given class.
isNotNull() Not null argument.
isNotNull(Class<T> clazz) Not null argument, not necessary of the given
class.
isNull() null argument.
isNull(Class<T> clazz) null argument.
notNull() Not null argument.
notNull(Class<T> clazz) Not null argument, not necessary of the given
class.
same(T value) Object argument that is the same as the given value.
46. verify(T mock)
verify(T mock, VerificationMode mode)
times(int num) num회 호출
never() 호출되지 않음
atLeastOnce() 최소한 한 번 이상 호출
atLeast(int min) min회 이상 호출
atMost(int max) max회 이하 호출
only() 마지막 호출
timeout(int millis) 지정한 시간 안에 처리 종료
verify(mockedList).add(“once");
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");
47. public static InOrder inOrder(Object... mocks)
//given
List singleMock = mock(List.class);
// when
singleMock.add("was added first");
singleMock.add("was added second");
// than
InOrder inOrder = inOrder(singleMock);
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
51. 작성된 JUnit 테스트를 실행
org.junit.runner.Runner 구현
@RunWith
JUnit에서 기본으로 여러가지 실
행기 제공
기본은 org.junit.runners.JUnit4
클래스
Runner
ParentRunner
Suite
BlockJUnit4-
ClassRunner
JUnit4
Theories
Categories
Enclosed
Parameterized
52. 한 클래스에 여러 테스트 클래스가 포함되어 있을 때 사용
org.junit.runner.Enclosed
@RunWith(Enclosed.class)
@RunWith(Enclosed.class)
public class SomeTest {
public class SomeInnerTest {
@Test public void test() {…}
}
public class AnotherInnerTest {
@Test public void test() {…}
}
}
53.
(Test Suite)
여러 테스트를 한 묶음으로 모아서 실행하려고 할 때 사용
org.junit.runner.Suite 실행기로 실행
@RunWith(Suite.class)
@Suite.SuiteClasses로 한 묶음으로 묶을 테스트
클래스 또는 테스트 모음 클래스 지정
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestFeatureLogin.class, TestFeatureLogout.class,
TestFeatureNavigate.class, TestFeatureUpdate.class })
public class FeatureTestSuite { }
54. 한 테스트 모음을 여러 분류로 구분해서 독립적으로 실행
@Category로 테스트 클래스나 테스트 메서드를 특정 분류로 지정 (ex
@Category(UnitTest.class))
@IncludeCategory와 @ExcludeCategory로 테스트에 포함시킬 테스트
지정
org.junit.runner.Categories
@RunWith(Categories.class)
@RunWith(Categories.class)
@IncludeCategory(CommunityVersion.class)
@ExcludeCategory(EnterpriseVersion.class)
@SuiteClasses( { A.class, B.class })
public class FeatureTestSuite {
}
55. 테스터가 입력값과 기대값을 표 형태로 제공하고 테스트를
수행
경계 값 등 여러 값의 조합으로 다양한 시나리오를 제공 가능
Junit에서는 매개변수화 테스트와 이론으로 지원
user id location time benefit
park 명동 9:00 12
kim 강남 12:00 4
lee 홍대 10:00 9
choi 부산 22:00 0
56.
(Parameterized Test)
테스트 클래스를 생성할 때 여러가지 초기 상태를 지정하고 반복 수행
org.junit.runners.Parameterized 실행기 사용
@Parameters 어노테이션으로 인자 목록 정의
name 속성으로 테스트 이름 부여 가능
{index} : 인자 순번
{#}: 0부터 시작, 인자 값
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameterized.Parameters(name="{index}: fib({0})={1}")
public static Collection<Object[]> args() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }
});
}
……
}
57. private int input;
private int expected;
public FibonacciTest(int input, int expected) {
this.input = input;
this.expected = expected;
}
@Parameterized.Parameter(0)
public int input;
@Parameterized.Parameter(1)
public int expected;
58.
(Theories)
무한대의 데이터에 대한 다양한 조합으로 테스트 수행
org.junit.runners.Theories 실행기 사용
: @DataPoint, @DataPoints
@FromDataPoints
assumeXxx
수행할 테스트에는 @Test 대신 @Theory 표시
@RunWith(Theories.class)
public class AdultValidationTest {
@DataPoints(“uid”) public static long[] UIDS =
new long[] { 10, 33, 452, 4321 };
@DataPoints(“cid”) public static long[] CIDS =
new long[] { 1, 53, 100, 984, 1343 };
@Theory public void validate(@FromDataPoints("uid")long userId,
@FromDataPoints("cid")long contentId){
User user = UserService.findById(userId);
assumeTrue(user.isAdult());
Content content = ContentService.findById(contentId);
assertTrue(content.isValid(user));
}
}
59.
60. JUnit
public class MemberJUnitWithSpringTest {
AnnotationConfigApplicationContext appctx;
MemberService memberService;
@Before
public void setUp() {
appctx = new AnnotationConfigApplicationContext(Config.class);
memberService = appctx.getBean(MemberService.class);
}
@Test
public void test() throws Exception {
assertEquals(memberService.getMember(1).getName(), " ");
assertEquals(memberService.getMember(2).getName(), " ");
assertEquals(memberService.getMember(3).getName(), " ");
}
@After
public void teardown() {
appctx.close();
}
}
61. 애플리케이션 컨텍스트 관리와 캐시
테스트 설비 의존관계 주입
트랜잭셕 관리
통합 테스트에 유용한 스프링의 클래스 라이브러리 제공
62. @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class)
public class MemberJUnitWithManualSpringTest {
@Autowired
public MemberService memberService;
@Test
public void test() throws Exception {
assertEquals(memberService.getMember(1).getName(), " ");
assertEquals(memberService.getMember(2).getName(), " ");
assertEquals(memberService.getMember(3).getName(), " ");
}
}
SpringJUnit4ClassRunner
63. @DirtiesContext
public void test() throws Exception { …… }
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class ContextDirtyingTests {
@DirtiesContext
public class ContextDirtyingTests {
초기화가 오래 걸리는 애플리케이션 컨텍스트 재사용
@DirtiesContext:
,
64. @Transactional을 테스트 클래스에 지정할 경우 매 테스트마다 자동으로 원복
(rollback) 처리가 됨
@TransactionConfiguration으로 빈 설정과 별도로 테스트 클래스 수준의 트
랜잭션 설정 추가 가능
@TransactionConfiguration(transactionManager = “txMgr",
defaultRollback = false)
@Transactional
public class CustomConfiguredTransactionalTests {
@Rollback로 클래스 수준의 기본 트랜잭션 설정을 메서드 단위에서 변경 가능
@Rollback(false)
public void testProcessWithoutRollback() { …… }
(commit)
65. 트랜잭션 전후에 로직 실행 가능
@BeforeTransaction
public void beforeTransaction() {
……
}
@AfterTransaction
public void afterTransaction() {
……
}
public class PartiallyTransactionalTests {
@Transactional
public void testProcessWithTransaction(){ …… }
public void testProcessWithoutTransaction(){ …… }
66. @WebAppConfiguration을 지정한 테스트 클래스의 애플리케이션 컨텍스트는
WebApplicationContext가 됨
기본으로 “file:src/main/webapp"을 웹 루트 경로로 사용
웹 루트 경로를 @WebAppConfiguration의 인자로 지정 가능
MockHttpServletRequest나 MockHttpSession을 주입 받고 리퀘스트와 세
션 스코프 빈을 테스트 할 수 있음
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public void ScopedBeanTests() {
@Autowired WebApplicationContext wac;
@Autowired MockHttpServletRequest request;
……
}
WebApplicationContext
HttpServletRequest
70. MVC
스프링 MVC 테스트 프레임워크
유창한(fluent) API로 서술적으로 테스트 조건과 단정문을 작성
서블릿 컨테이너 없이 스프링 MVC의 모든 동작과 뷰(View) 처
리 결과까지 테스트
MockMvcBuilders를 사용해서 생성
mockMvc = standaloneSetup(new AccountController())
.defaultRequest(get("/")
.contextPath("/app").servletPath("/main")
.accept(MediaType.APPLICATION_JSON).build();
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
Web Application Context
77. • Handler type test ( Handler
)
• .andExpect(handler().handlerType(..))
• Handler method name test ( Handler
)
• .andExpect(handler().methodName(..))
• Handler method type test ( Handler
)
• .andExpect(handler().method(..))
78. • Model attrbute test ( Model )
• .andExpect(model().attribute(..))
• .andExpect(model().attributeExists(..))
• .andExpect(model().attributeErrorCount(..))
• .andExpect(model().attributeHasErrors(..))
• .andExpect(model().attributeHasNoErrors(..))
• .andExpect(model().attributeHasFieldErrors(..))
• .andExpect(model().errorCount(..))
• .andExpect(model().hasErrors(..))
• .andExpect(model().hasNoErrors(..))
79. • Status test ( )
• .andExpect(status().is(..))
• ( 200: … )
• org.springframework.http HttpStatus
• Error message test ( )
• .andExpect(status().reason(..))
• .
• Header test ( )
• .andExpect(header().string(..))
• .andExpect(header().longValue(..))
80. • ContentType test( contentType )
• .andExpect(content().contentType(..))
• .andExpect(content().contentTypeCompatibleWith(..))
• Encoding test ( content )
• .andExpect(content().encoding(..))
• Content test ( content )
• .andExpect(content().string(..)) : content
• .andExpect(content().bytes(..)) : content byte
• .andExpect(content().xml(..)) : content xml dom
• .andExpect(content().node(..)) : content xml dom node
• .andExpect(content().source(..)) : content xml dom source
81. • View name test ( View name )
• .andExpect(view().name(..))
• Forwarded url test( URL )
• .andExpect(forwardedUrl(..))
• Redirected url test ( URL )
• .andExpect(redirectedUrl(..))
• Flash attribute test ( Spring 3.1 Flash
attribute )
• .andExpect(flash().attribute(..)) : attribute
• .andExpect(flash(). attributeExists(..)) : attribute
• .andExpect(flash(). attributeCount(..)) : attribute