Hamcrest
:JUnit 기반의 단위 테스트에서 사용할 수 있는 Assertion Framework
? Hamcrest 사용하는 이유?
:Assertion을 위한 매쳐Matcher가 자연스러운 문장으로 이어지므로 가독성이 향상됨
:테스트 실패 메시지를 이해하기 쉬움
:다양한 Matcher를 제공
ex1.
-ver Junit에서의 Assertion
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class HelloJunitTest {
@DisplayName("Hello Junit Test")
@Test
public void assertionTest1() {
String actual = "Hello, JUnit";
String expected = "Hello, JUnit";
assertEquals(expected, actual); // 1)
}
}
-ver Hamcrest의 Matcher
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
public class HelloHamcrestTest {
@DisplayName("Hello Junit Test using hamcrest")
@Test
public void assertionTest1() {
String expected = "Hello, JUnit";
String actual = "Hello, JUnit";
assertThat(actual, is(equalTo(expected))); // 2)
}
}
1) assertEquals(expected, actual);
:JUnit Assertion 기능 이용
:파라미터로 입력된 값의 변수 이름을 통해 대략적으로 어떤 검증을 하려는지 알 수 있으나 구체적인 의미는 유추하는 과정이 필요
2) assertThat(actual, is(equalTo(expected)));
:Hamcrest의 매쳐Matcher 이용
:위 코드는 assert that actual is equal to expected
라는 문장으로 자연스럽게 읽힘(결과값actual이 기대값expected과 같다는 것을 검증Assertion함)
-assetThat() 메서드의 파라미터
:첫번째 파라미터 :테스트 대상의 실제 결과 값
:두번째 파라미터 :기대하는 값, 이런 값일거라고 기대(예상)하는 값
ex2.
-ver Junit에서의 Assertion
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class HelloJunitTest {
@DisplayName("Hello Junit Test")
@Test
public void assertionTest1() {
String actual = "Hello, JUnit";
String expected = "Hello, World";
assertEquals(expected, actual);
}
}
결과
:failed
expected: <Hello, World> but was: <Hello, JUnit>
Expected :Hello, World
Actual :Hello, JUnit
-ver Hamcrest의 Matcher
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
public class HelloHamcrestTest {
@DisplayName("Hello Junit Test using hamcrest")
@Test
public void assertionTest() {
String expected = "Hello, World";
String actual = "Hello, JUnit";
assertThat(actual, is(equalTo(expected)));
}
}
결과
:failed
Expected: is "Hello, World"
but: was "Hello, JUnit"
:실행 결과도 하나의 문장으로 자연스럽게 읽혀짐
ex3.
-테스트 대상 클래스
import java.util.HashMap;
import java.util.Map;
public class CryptoCurrency {
public static Map<String, String> map = new HashMap<>();
static {
map.put("BTC", "Bitcoin");
map.put("ETH", "Ethereum");
map.put("ADA", "ADA");
map.put("POT", "Polkadot");
}
}
-ver Junit에서의 Assertion
import com.codestates.CryptoCurrency;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class AssertionNullHamcrestTest {
@DisplayName("AssertionNull() Test")
@Test
public void assertNotNullTest() {
String currencyName = getCryptoCurrency("ETH");
assertNotNull(currencyName, "should be not null");
}
private String getCryptoCurrency(String unit) {
return CryptoCurrency.map.get(unit);
}
}
-ver Hamcrest의 Matcher
import com.codestates.CryptoCurrency;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
public class AssertionNullHamcrestTest {
@DisplayName("AssertionNull() Test")
@Test
public void assertNotNullTest() {
String currencyName = getCryptoCurrency("ETH");
assertThat(currencyName, is(notNullValue())); // 1)
// assertThat(currencyName, is(nullValue())); // 2)
}
private String getCryptoCurrency(String unit) {
return CryptoCurrency.map.get(unit);
}
}
NotNull Test
:is(), notNullValue() 매쳐 사용
2) assertThat(currencyName, is(nullValue())) 주석 해제 시 결과는 아래와 같음
Expected: is null
but: was "Ethereum"
ex4.
-ver Junit에서의 Assertion
import com.codestates.CryptoCurrency;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class AssertionExceptionTest {
@DisplayName("throws NullPointerException when map.get()")
@Test
public void assertionThrowExceptionTest() {
assertThrows(NullPointerException.class, () -> getCryptoCurrency("XRP"));
}
...
...
private String getCryptoCurrency(String unit) {
return CryptoCurrency.map.get(unit).toUpperCase();
}
}
:assertThrows() 이용해 "XRP" 존재하는지 여부를 확인하는 예외 발생 여부를 테스트
-ver Hamcrest의 Matcher
import com.codestates.CryptoCurrency;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class AssertionExceptionHamcrestTest {
@DisplayName("throws NullPointerException when map.get()")
@Test
public void assertionThrowExceptionTest() {
Throwable actualException = assertThrows(NullPointerException.class,
() -> getCryptoCurrency("XRP")); // 1)
assertThat(actualException.getClass(), is(NullPointerException.class)); // 2)
}
private String getCryptoCurrency(String unit) {
return CryptoCurrency.map.get(unit).toUpperCase();
}
}
:발생하는 예외가 NullPointerException인지 체크
but.
예외에 대한 테스트는 Hamcrest만으로 Assertion을 구성하기 힘듦
:1)과 같이 JUnit의 assertThrows() 메서드를 이용해 assertThrows() 리턴값을 전달받은 후
:2)와 같이 assertThat(actualException.getClass(),is(NullPointerExceptioin.class));를 통해 throw된 Exception 타입이 기대했던 Exception 타입과 일치하느지 추가로 검증 진행