์ด๋ค ๋์์ ๋ํ ์ผ์ ๊ธฐ์ค์ ์ ํด๋๊ณ , ๊ทธ ๋์์ด ์ ํด์ง ๊ธฐ์ค์ ๋ถํฉํ๋์ง ๋ถํฉํ์ง ๋ชปํ๋์ง๋ฅผ ๊ฒ์ฆํ๋ ๊ณผ์
JUnit
ํ
์คํธ ํ๋ ์์ํฌ๋ก ํ
์คํ
โ๏ธ ๋จ์ ํ ์คํธ
์์ฃผ ์์ ๋จ์์ ๊ธฐ๋ฅ์ ํ ์คํธ ํ๋ ๋ฐฉ๋ฒ
๋๋ถ๋ถ ๋ฉ์๋ ๋จ์์ ์ฝ๋๋ก ์์ฑ
๋จ์ ํ
์คํธ๋ฅผ ์ ์ผ ์ฝ๊ณ ๋น ๋ฅด๊ฒ ์ ์ฉํ ์ ์๋ ๋ถ๋ถ
โ ํฌํผ(helper) ํด๋์ค or ์ ํธ๋ฆฌํฐ(utility) ํด๋์ค
๐ก But, ์ ํธ๋ฆฌํฐ ํด๋์ค๋ค์ ๋ฉ์๋๋ค์ ํด๋์ค์ ๊ฐ์ฒด๋ก ์ธ์คํด์คํ ๋ ํ์๊ฐ ์์ด ๋ชจ๋ ์ ์ (static)๋ฉ์๋๋ก ๊ตฌ์ฑ
โ ๊ฐ์ฒด ์งํฅ์ ์ธ ์๊ฐ์ผ๋ก ์ฌ์ฉ ์ง์ But, ๋ง์ด ์ฌ์ฉํจ
๋จ์ํ ์คํธ ์ฅ์
๊ตฌํ ์ฝ๋๊ฐ ์๋๋๋ก ๋์ํ๋์ง ๋น ๋ฅด๊ฒ ๊ฒฐ๊ณผ ํ์ธ ๊ฐ๋ฅ
๋ฒ๊ฑฐ๋ก์ด ํ ์คํธ ์ผ๋ค ๋จ์ํ
์์ ๋จ์๋ก ๋ฏธ๋ฆฌ ๋ฒ๊ทธ๋ฅผ ์ฐพ์๋ผ ์ ์์ด ์๋์ ์ผ๋ก ๋ ์ ์ ์๊ฐ ๋ด์ ๋ฌธ์ ์ฐพ๊ธฐ ๊ฐ๋ฅ
๐ฌ ๊ทธ๋ผ DB๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋จ์ ํ ์คํธ๋ผ๊ณ ๋ณด๊ธฐ ํ๋ค๊น?
โ ๋จ์ ํ ์คํธ๋ ์ต๋ํ ๋ ๋ฆฝ์ ์ด๊ณ ์์ ๋จ์์ธ ๊ฒ์ด ์ข์ง๋ง,
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํ๊ฐ ํ ์คํธ ์ด์ ๊ณผ ์ดํ๊ฐ ๋์ผํ๊ฒ ์ ์ง๋ ์ ์๋ค๋ฉด, ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ฐ๋๋๋ค๊ณ ํด๋ ๋จ์ ํ ์คํธ์ ํฌํจ๋ ์๋ ์์
โ๏ธ ๊ธฐ๋ฅ ํ ์คํธ
์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉํ๋ ์ฌ์ฉ์ ์ ์ฅ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๋์ง๋ฅผ ํ ์คํธ
๊ฐ๋ฐํ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ฐ๊ด๋ ๋์์ด ๋ง์ ๋จ์ ํ ์คํธ๋ผ ๋ถ๋ฅด๊ธฐ ํ๋ฆ
ํ
์คํธ๋ฅผ ํ๋ ์ฃผ์ฒด๊ฐ ์ฃผ๋ก ๊ฐ๋ฐ์ ์ด์ธ์ ์ 3์
Ex. ํ
์คํธ ์ ๋ฌธ ๋ถ์(QA ๋ถ์), ์ธ๋ถ QA ์
์ฒด ๋ฑ
โ๏ธ ํตํฉ ํ ์คํธ
ํด๋ผ์ด์ธํธ ์ธก ํด ์์ด, ๊ฐ๋ฐ์๊ฐ ์ง ๋์ ์ฝ๋๋ฅผ ์คํ์์ผ ํ๋ ํ ์คํธ
์ฌ๋ฌ ๊ณ์ธต๊ณผ ์ฐ๊ด๋์ด ์์ผ๋ฏ๋ก ๋จ์ ํ ์คํธ๋ผ ๋ถ๋ฅด๊ธฐ ํ๋ฆ
โ๏ธ ์ฌ๋ผ์ด์ค ํ ์คํธ
์ ํ๋ฆฌ์ผ์ด์
์ ํน์ ๊ณ์ธต์ผ๋ก ์ชผ๊ฐ์ ํ๋ ํ
์คํธ
Ex. API ๊ณ์ธต / ์๋น์ค ๊ณ์ธต / ๋ฐ์ดํฐ ์์ธ์ค ๊ณ์ธต
๋จ์ ํ ์คํธ๋ผ๊ณ ๋ถ๋ฅด๊ธฐ์ ๋จ์๊ฐ ํฐ ํ ์คํธ
์ ํ๋ฆฌ์ผ์ด์ ์ ์ผ๋ถ๋ง ํ ์คํธํ๊ธฐ ๋๋ฌธ์ ๋ถ๋ถ ํตํฉ ํ ์คํธ๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํจ
โ๏ธ ์ค๋ชจํฌ ํ ์คํธ (Smoke Test)
โ๏ธ ํ ์คํธ ์ผ์ด์ค (Test Case)
โ ํ๋์ ๋จ์๋ฅผ ํ ์คํธํ๊ธฐ ์ํด ์์ฑํ๋ ํ ์คํธ ์ฝ๋
Fast (๋น ๋ฅด๊ฒ)
โ ํ
์คํธ ์ผ์ด์ค๋ ๋์์ด ๋นจ๋ผ์ผํจ
Independent (๋
๋ฆฝ์ ์ผ๋ก)
โ ๊ฐ ํ
์คํธ๊ฐ ๋
๋ฆฝ์ ์ด์ด์ผ ํจ
( ๊ฒฐ๊ณผ๋ ๊ฐ๊ฐ์ ์ํญ์ ๋ฏธ์น์ง ์์์ผ ํจ )
Repeatable (๋ฐ๋ณต ๊ฐ๋ฅํ๋๋ก)
โ ์ด๋ค ํ๊ฒฝ์์๋ ๋ฐ๋ณต ์คํ์ด ๊ฐ๋ฅํด์ผํจ
โ ์ธ๋ถ ์๋น์ค / ์ธ๋ถ ๋ฆฌ์์ค๊ฐ ์ฐ๋๋ ๊ฒฝ์ฐ ๋์ผํ ํ
์คํธ ๊ฒฐ๊ณผ ๋ณด์ฅ X
Self-validating (์
ํ ๊ฒ์ฆ์ด ๋๋๋ก)
โ ํ
์คํธ ์ผ์ด์ค ์ค์ค๋ก ๊ฒฐ๊ณผ๊ฐ ์ฑ๊ณต / ์คํจ ์ธ์ง๋ฅผ ํ๋จํ ์ ์์ด์ผํจ
Timely (์๊ธฐ ์ ์ ํ๊ฒ)
โ ํ
์คํธํ๋ ค๋ ๊ธฐ๋ฅ ๊ตฌํ ์ง์ ์ ์์ฑํด์ผํจ (TDD (ํ
์คํธ ์ฃผ๋ ๊ฐ๋ฐ))
( ๊ตฌํํ๊ณ ์ ํ๋ ๊ธฐ๋ฅ์ ๋ค ์์ฑํ๊ณ ์คํ์ํค์ง ์๊ณ ์งง๊ฒ ์์ฑํ๊ณ ๋๋ ค๋ณด๊ณ ์ ์ ์
๊ทธ๋ ์ด๋ํ๋ ์์ผ๋ก )
Given - When - Then ํํ ์คํ์ผ
( BDD ํ
์คํธ ๋ฐฉ์์์ ์ฌ์ฉํ๋ ์ฉ์ด )
โ๏ธ BDD (Behavior Driven Development)
- ํ๋ ์ค์ฌ ๊ฐ๋ฐ
- ์ํํธ์จ์ด ํ๋ก์ ํธ์์ ๊ฐ๋ฐ์, ํ์ง ๋ณด์ฆ ์ ๋ฌธ๊ฐ ๋ฐ ๊ณ ๊ฐ ๋ด๋น์ ๊ฐ์ ํ์ ์ ์ฅ๋ คํ๋ ๋ฏผ์ฒฉํ ์ํํธ์จ์ด ๊ฐ๋ฐ ํ๋ก์ธ์ค
[์ฐธ๊ณ ] https://en.wikipedia.org/wiki/Behavior-driven_development
Given
ํ ์คํธํ๊ณ ์ ํ๋ ๋์
ํ ์คํธ๋ฅผ ์ํ ์ ์ ์กฐ๊ฑด๋ค์ด ํฌํจ
ํ ์คํธ ๋์์ ์ ๋ฌ๋๋ ์ ๋ ฅ ๊ฐ(ํ ์คํธ ๋ฐ์ดํฐ)๋ ํฌํจ
When
Then
ํ ์คํธ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ์ฆ (Assertion)
์์ํ๋ ๊ฐ(expected)๊ณผ ํ ์คํธ ๋์ ๋ฉ์๋์ ๋์ ์ํ ๊ฒฐ๊ณผ(actual) ๊ฐ์ ๋น๊ตํด์ ๊ธฐ๋ํ๋๋ก ๋์์ ์ํํ๋์ง ๊ฒ์ฆ(Assertion)ํ๋ ์ฝ๋๋ค
โ๏ธ Assertion (๊ฒ์ฆ)
- ํ ์คํธ ์ผ์ด์ค์ ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋์ ์ฐธ(true)์ด์ด์ผ ํ๋ค๋ ๊ฒ์ ๋ ผ๋ฆฌ์ ์ผ๋ก ํํํ ๊ฒ
Java ์ธ์ด๋ก ๋ง๋ค์ด์ง ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธ ํ๊ธฐ ์ํ ์คํ ์์ค ํ ์คํธ ํ๋ ์์ํฌ
Java์ ํ์ค ํ ์คํธ ํ๋ ์์ํฌ๋ผ๊ณ ํด๋ ๊ณผ์ธ์ด ์๋
TestNG
๋ผ๋ JUnit์ ๊ฐ๋ ฅํ ๊ฒฝ์์๊ฐ ์๊ธดํ์ง๋ง, Spring Boot์ ๋ํดํธ ํ
์คํธ ํ๋ ์์ํฌ๋ JUnit
assertXXXX()
๋ก ์์ํ๋ ๋ค์ํ Assertion ๋ฉ์๋๋ฅผ ์ง์
JUnit์ผ๋ก ์์ฑํ ํ
์คํธ ์ผ์ด์ค๋ ํญ์ ์ผ์ ํ ์์๋ก ํ
์คํธ ์ผ์ด์ค ์คํ X ( ์์ ๋๋ค )
โ ๊ฐ๊ฐ ํ
์คํธ ์ผ์ด์ค์ ๋
๋ฆฝ์ฑ์ด ๋ณด์ฅ๋์ด์ผ ํจ
Ex. ํ ํ ์คํธ ์ผ์ด์ค์์ ํ ์คํธ๋ฅผ ์ํด ์ ๋ณด๋ฅผ delete ํ๋ค๋ฉด, ๋ค๋ฅธ ํ ์คํธ์๋ ์ด deleteํ ํ์๊ฐ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ก ๋ค์ ์ฒ์์ผ๋ก ์ด๊ธฐํ ๋์ด์ผ ํจ
โ@Transactional
์ ๋ํ ์ด์ ๋ถ์
๐ก Spring Boot Initializer ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ ์์ฑํ๋ค๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก
testImplementation >'org.springframework.boot:spring-boot-starter-test'
์คํํฐ๊ฐ ํฌํจ๋๋ฉฐ, ์๋์ผ๋ก JUnit๋ ํฌํจ
public class JunitDefaultStructure {
@Test
public void test1() {
// ํ
์คํธ ํ๊ณ ์ ํ๋ ๋์์ ๋ํ ํ
์คํธ ๋ก์ง ์์ฑ
}
@Test
public void test2() {
// ํ
์คํธ ํ๊ณ ์ ํ๋ ๋์์ ๋ํ ํ
์คํธ ๋ก์ง ์์ฑ
}
.
.
.
}
โ ์์ ๊ฐ์ด void ํ์
์ ๋ฉ์๋๋ฅผ ๋ง๋ค๊ณ ๊ทธ์ @Test
์ ๋ํ
์ด์
์ ์ถ๊ฐํด์ค์ผ ํจ
@DisplayName("ํ์ํ ์ด๋ฆ")
์ ๋ํ
์ด์
์ ๋ถ์ฌ์ฃผ๋ฉด ์๋์ ๊ฐ์ดโ ํ ์คํธ์์ ์์ธ๊ฐ ๋ฐ์ํ๋ค๊ณ ๋ฌด์กฐ๊ฑด failed๊ฐ ์๋,
์ฐ๋ฆฌ๊ฐ ๊ธฐ๋ํ ๊ฐ(ํต๊ณผ or ์์ธ)์ด ๊ฒฐ๊ณผ ๊ฐ๊ณผ ์ผ์นํ๋์ง์ ๋ฐ๋ผ passed / failed๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์์ ์๊ธฐ !
โ ํ ์คํธ๊ฐ ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ ๋๋ก ๋ฆฌํดํ๋์ง ์๋ฌ๋ ๋ฐ์ํ์ง ์๋์ง ํ์ธํ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋
โ ๊ฐ๋ฐ์๊ฐ ํ ์คํธํ๊ณ ์ถ์ ์ธ์๊ฐ์ ๋ฃ์์ ๋ ์์ํ ๊ฒฐ๊ณผ๊ฐ ๋์ค๋์ง ํ ์คํธ ํด๋ณผ ๊ฒฝ์ฐ ์ฌ์ฉ ( ์ฌ์ค์์ ์ฃผ์ฅ )
assertEquals(๊ธฐ๋ํ๋ ๋ฌธ์์ด, ์ค์ ๊ฒฐ๊ณผ ๊ฐ)
โ ๊ธฐ๋ํ๋ ๊ฐ๊ณผ ์ค์ ๊ฒฐ๊ณผ ๊ฐ์ด ์ผ์นํ๋์ง ๊ฒ์ฆ
assertTrue(์์ํ ๋์)
โ ๋์์ด true์ธ์ง ๊ฒ์ฆ
assertFalse(์์ํ ๋์)
โ ๋์์ด false์ธ์ง ๊ฒ์ฆ
assertNotNull(ํ
์คํธ ๋์ ๊ฐ์ฒด, ํ
์คํธ ์คํจ์ ํ์ํ ๋ฉ์ธ์ง)
โ Null์ด ์๋์ง ์ฌ๋ถ ํ
์คํธ
( Null์ด ์๋๋ฉด passed / Null์ด๋ฉด failed )
assertNull(ํ
์คํธ ๋์ ๊ฐ์ฒด, ํ
์คํธ ์คํจ์ ํ์ํ ๋ฉ์ธ์ง)
โ Null์ธ์ง ์ฌ๋ถ ํ
์คํธ
( Null์ด๋ฉด passed / Null์ด ์๋๋ฉด failed )
assertThrows(๋ฐ์์ด๋ ๊ฒ์ผ๋ก ์์๋๋ ์์ธ ํด๋์ค, ๋๋ค์์ผ๋ก ํ
์คํธ ๋์ ๋ฉ์๋ ํธ์ถ)
โ ์์ํ ์์ธ(Exception)๊ฐ ๋ฐ์ํ๋์ง ํ
์คํธ
Ex.
@Test public void assertionThrowExceptionTest() { assertThrows(NullPointerException.class, () -> getColor("RED")); } โ private String getColor(String unit) { return Color.map.get(unit).toUpperCase(); }
์์ ์์์์
RED
๋ผ๋ ์์ด ์์ผ๋ฉด assertThrows()๋ null์ด๋ผ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ ๊ฒ.
๊ทธ๋ฐ๋ฐ ๋ฐ์์ null์ธ ๊ฐ์toUpperCase()
๋ฉ์๋๋ก ๋๋ฌธ์๋ก ๋ณํํ๋ ค๊ณ ํ๊ธฐ ๋๋ฌธ์
๊ฒฐ๊ตญ์ NullPointerException์ด ๋ฐ์ํ๊ฒ ๋จ
โ ์ฐ๋ฆฌ๊ฐ ์์ํ ์์ธ ๊ฐ์ดNullPointerException
์ด ๋ง๊ธฐ ๋๋ฌธ์ ํ ์คํธ passed
โ
โassertThrows()
๋ฉ์๋ ๋ด์ ExpectedType ์ ์์ ํ์ ์ ์ธRuntimeException.class
์ด๋Exception.class
์ผ๋ก ์ ๋ ฅํด๋ ๊ฒฐ๊ณผ๋ passed !
(NullPointerException
extendsRuntimeException
/RuntimeException
extendsException
)
โ ์์ธ ํด๋์ค์ ์์ ๊ด๊ณ๋ฅผ ์ดํดํ ์ํ์์ ํ ์คํธ ์คํ ๊ฒฐ๊ณผ ์์ํด์ผํจ
[์ฐธ๊ณ ] https://beomseok95.tistory.com/205
[Assertions ์ฐธ๊ณ ] https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
โ ๋ฉ์๋๋ณ ์กฐ๊ฑด์ ๋ง์กฑํ ๊ฒฝ์ฐ ์งํ์ํค๊ณ ์๋ ๊ฒฝ์ฐ ์คํตํ๋ ๋ฉ์๋
โ ๊ฐ๋ฐ์๊ฐ ์ธ์๊ฐ์ ์ ํํ ๋ชจ๋ฅผ ๋ if ์ ๊ฐ์ ์ฉ๋๋ก ์ฌ์ฉ ( ๊ฐ์ / ์ถ์ )
assumeTrue()
โ ํ๋ผ๋ฏธํฐ๋ก ์
๋ ฅ๋ ๊ฐ์ด true์ด๋ฉด ๋๋จธ์ง ์๋ ๋ก์ง๋ค์ ์คํ
assumeFalse()
โ ํ๋ผ๋ฏธํฐ๋ก ์
๋ ฅ๋ ๊ฐ์ด false์ด๋ฉด ๋๋จธ์ง ์๋ ๋ก์ง๋ค์ ์คํ
[Assunptions ์ฐธ๊ณ ] https://junit.org/junit5/docs/current/user-guide/#writing-tests-assumptions
[Assertion / Assumption ์ฐจ์ด ์ฐธ๊ณ ] https://effortguy.tistory.com/123
@BeforeEach
ํด๋ ์ค ๋ ๋ฒจ์ ์ฌ์ฉ
๊ฐ ํ
์คํธ ์ผ์ด์ค๊ฐ ์คํ๋ ๋ ๋ง๋ค ์ด ์ ๋ํ
์ด์
์ ๋ถ์ธ ๋ฉ์๋์ ๋ด์ฉ๋ค์ด ์คํ๋ ํ, ํด๋น ํ
์คํธ ์ผ์ด์ค ์คํ
โ
Ex.
public class BeforeEachTest { private Map<String, String> map; โ @BeforeEach public void init() { map = new HashMap<>(); map.put("RED", "Red"); map.put("BLA", "Black"); } โ @DisplayName("Test case 1") @Test public void beforeEachTest1() { map.put("WHI", "White"); assertDoesNotThrow(() -> getCryptoCurrency("WHI")); // ๊ฒฐ๊ณผ --> passed ( Assertion ํ๊ธฐ ์ ์ map ์ โXRPโ๋ฅผ ์ถ๊ฐํ๊ธฐ ๋๋ฌธ ) } โ // Test case 2 ์คํ๋๊ธฐ ์ , init() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด์ map ๊ฐ์ฒด ๋ค์ ์ด๊ธฐํ // --> ์ ํ ์คํธ ์ผ์ด์ค์์ WHI ์ถ๊ฐ ํ๋๋ผ๋ ๋ค์ ์ด๊ธฐํ๋์ด ์ด์ ์ํ๋ก ๋๋์๊ฐ @DisplayName("Test case 2") @Test public void beforeEachTest2() { System.out.println(map); assertDoesNotThrow(() -> getColor("WHI")); // ๊ฒฐ๊ณผ --> failed // ์์ธ ๋ฐ์ํ์ง ์์ ๊ฒ์ด๋ผ๊ณ ๊ธฐ๋ํ๋๋ฐ, Assertion ํ๊ธฐ ์ ์ // map ์ โWHIโ๋ฅผ ์ถ๊ฐํ์ง ์์์ ์์ ๊ฐ์ด ์์ด์ ์์ธ ๋ฐ์ํ๊ธฐ ๋๋ฌธ } โ private String getColor(String unit) { return map.get(unit).toUpperCase(); } }
@BeforeAll()
Ex.
public class BeforeAllTest { private static Map<String, String> map; โ @BeforeAll // ์ด ์ ๋ํ ์ด์ ์ ์ถ๊ฐํ ๋ฉ์๋๋ ์ ์ (static) ๋ฉ์๋์ฌ์ผ ํจ !! public static void initAll() { map = new HashMap<>(); map.put("RED", "Red"); map.put("BLA", "Black"); โ โ System.out.println("initialize Color map"); // ๊ฒฐ๊ณผ --> ๋ ์ผ์ด์ค ํตํ์ด์ ์ด๊ธฐํ๊ฐ ํ๋ฒ๋ง ๋๊ธฐ ๋๋ฌธ์ // "initialize Color map" ํ๋ฒ ์ถ๋ ฅ } โ @DisplayName("Test case 1") @Test public void beforeEachTest1() { map.put("WHI", "White"); assertDoesNotThrow(() -> getColor("WHI")); } โ @DisplayName("Test case 2") @Test public void beforeEachTest2() { assertDoesNotThrow(() -> getColor("WHI")); // ๊ฒฐ๊ณผ --> passed ์ด๊ธฐํ๊ฐ ํ ์คํธ1 ์งํ ์ ์ ํ๋ฒ ๋๊ธฐ ๋๋ฌธ์, // test1์์ ์ ์ฅํ ๊ฐ์ด ์ฌ๋ผ์ง์ง ์๊ณ ๋จ๊ฒ ๋์ด ์กฐํ ๊ฐ๋ฅ } โ private String getColor(String unit) { return map.get(unit).toUpperCase(); } }
@AfterEach
@AfterAll
( ์ ์ ์ฒ๋ฆฌ ์ ๋ํ ์ด์ ์ด๋ ๋์๋ฐฉ์์ ๊ฐ๊ณ , ํธ์ถ๋๋ ์์ ๋ง ๋ฐ๋ )
JUnit ๊ธฐ๋ฐ์ ๋จ์ ํ ์คํธ์์ ์ฌ์ฉํ ์ ์๋ Assertion Framework
Assertion์ ์ํ ๋งค์ณ(Matcher)์ ์ ๊ณต์ผ๋ก ์ฌ๋์ด ์ฝ๊ธฐ ํธํ ์์ฐ์ค๋ฌ์ด Assertion ๋ฌธ์ฅ ๊ตฌ์ฑ
โ ๊ฐ๋
์ฑ ํฅ์
ํ ์คํธ ์คํจ ๋ฉ์์ง๋ฅผ ์ดํดํ๊ธฐ ์ฌ์
โ ์์ ์ฅ์ ๋ค๋๋ฌธ์ JUnit์ ์ง์ํ๋ Assertion ๋ฉ์๋ ๋ณด๋ค ๋ ๋ง์ด ์ฌ์ฉ
๐ก But, ์์ธ์ ๋ํ ํ ์คํธ๋ Hamcrest ๋ง์ผ๋ก Assertion ๊ตฌ์ฑ์ด ํ๋ฆ
โ JUnit ์ assetThrows() ๋ฉ์๋ ์ด์ฉ
โ
- Hamcrest ๋ง์ผ๋ก ๋์ ธ์ง(thrown) ์์ธ๋ฅผ ํ ์คํธ ํ๊ณ ์ถ๋ค๋ฉด,
Custom Matcher๋ฅผ ์ง์ ๊ตฌํํด์ ์ฌ์ฉ
[์ฐธ๊ณ ] http://hamcrest.org/JavaHamcrest/tutorial
โ๏ธ
assertThat( ํ ์คํธ ๋์์ ์ค์ ๊ฒฐ๊ณผ๊ฐ, ์์ํ๋ ๊ฐ )
- assertEquals()๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ณด๋ค ํ๋์ ์์ด ๋ฌธ์ฅ์ผ๋ก ์์ฐ์ค๋ฝ๊ฒ ์ฝํ ๋ ๊ตฌ์ฒด์ ์ธ ์๋ฏธ ์ ์ถ ๊ฐ๋ฅ
- ์คํ ๊ฒฐ๊ณผ ๋ํ ๋ ์์ฐ์ค๋ฝ๊ฒ ์ฝํ
is()
โ ๊ฐ๋
์ฑ์ ๋์ด๊ธฐ ์ํ decorator
equalTo()
โ ๊ฐ์ฒด๊ฐ ๋์ผํ์ง ํ๋จ
Ex. ( ์๋ ์ธ ์์๋ ๋์ผํจ )
assertThat(theBiscuit, equalTo(myBiscuit));
assertThat(theBiscuit, is(equalTo(myBiscuit)));
assertThat(theBiscuit, is(myBiscuit));
hasToString()
โ ๋ฉ์๋ ๊ฐ๊ณผ ์ผ์น ์ฌ๋ถ ํ๋จ
notNullValue()
/nullValue()
โ Null์ธ์ง ์๋์ง ํ๋ณ
[์ฐธ๊ณ ] http://hamcrest.org/JavaHamcrest/javadoc/2.2/
[Matcher ์ข
๋ฅ ์ฐธ๊ณ ] https://velog.io/@hanblueblue/spring-boot-%EC%9C%A0%EB%8B%9B-%ED%85%8C%EC%8A%A4%ED%8A%B8-4.Hamcrest
Controller
โ๏ธ Controller ์ฌ๋ผ์ด์ค ํ ์คํธ์ ๋ชฉ์ !!
- ๊ตฌ์ฒด์ ์ธ ๋ก์ง์ ํ ์คํธํ๊ธฐ ์ํจ์ด ์๋,
HTTP request๋ถํฐ response ๊ตฌ๊ฐ๊น์ง ์ด์์์ด ๋์ํ๊ฑฐ DTO ์ ํจ์ฑ ๊ฒ์ฆ์ด ์ ์ผ์ด๋๋์ง ํ์ธํ๊ธฐ ์ํจ
( ์์ฒญ, ์๋ต์ด ์ ๋ค์ด์ค๊ณ DTO ์ ํจ์ฑ ๊ฒ์ฆ์ด ์ ์ผ์ด๋๋ )
โ๏ธ Controller ํ ์คํธ๋ฅผ ์ํ ํ ์คํธ ํด๋์ค ๊ตฌ์กฐ
@SpringBootTest @AutoConfigureMockMvc public class ControllerTestDefaultStructure { โ @Autowired private MockMvc mockMvc; โ @Test public void postMemberTest() { // ํ ์คํธํ๊ณ ์ ํ๋ Controller ํธ๋ค๋ฌ ๋ฉ์๋์ ํ ์คํธ ์ผ์ด์ค ์์ฑ โ // given // ํ ์คํธ์ฉ request body ์์ฑ โ // when // MockMvc ๊ฐ์ฒด๋ก ํ ์คํธ ๋์ Controller ํธ์ถ // ( MockMvc ๊ฐ์ฒด๋ฅผ ํตํด ์์ฒญ URI / HTTP ๋ฉ์๋๋ฑ ์ง์ ํ๊ณ , given ์์ ๋ง๋ // ํ ์คํธ์ฉ request body ๋ฅผ ์ถ๊ฐํ ๋ค, request ์ํ ) โ // then // Controller ํธ๋ค๋ฌ ๋ฉ์๋์์ ์๋ต์ผ๋ก ์์ ํ HTTP Status / response body ๊ฒ์ฆ } }
@SpringBootTest
Spring Boot ๊ธฐ๋ฐ์ ์ ํ๋ฆฌ์ผ์ด์
์ ํ
์คํธ ํ๊ธฐ ์ํ Application Context ์์ฑ
( Application Context ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ํ์ํ Bean ๊ฐ์ฒด๋ค์ด ๋ฑ๋ก๋์ด ์์ )
ํตํฉ ํ ์คํธ์ ์ ํฉ
@AutoConfigureMockMvc
MockMvc
Tomcat ๊ฐ์ ์๋ธ๋ฆฟ ์ปจํ ์ด๋๋ฅผ ์คํํ์ง ์๊ณ Spring ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ Controller ๋ฅผ ํ ์คํธ ํ ์ ์๋ ์๋ฒฝํ ํ๊ฒฝ์ ์ง์ํด์ฃผ๋ ์ผ์ข ์ Spring MVC ํ ์คํธ ํ๋ ์์ํฌ
@AutoConfigureMockMvc
์ ๋ํ
์ด์
์ ๊ผญ ๋ฌ์์ผ ์ฌ์ฉ ๊ฐ๋ฅ
โ๏ธ
@WebMvcTest
๋ฅผ ์ด์ฉํ Controller ํ ์คํธ
- Controller๋ฅผ ํ ์คํธ ํ๊ธฐ ์ํ ์ ํต์ ์ธ ๋ฐฉ๋ฒ
- ์น ๊ณ์ธต ์ ์ฉ ์ฌ๋ผ์ด์ค ํ ์คํธ์ ์ ํฉ
- ํ ์คํธํ๋ ๋ฐ ํ์ํ ๋น๋ค๋ง ๋ฑ๋กํด์ ์ฌ์ฉ
โ
But, Controller์์ ์์กดํ๋ ์ปดํฌ๋ํธ๋ค์ ๋ชจ๋ ์ผ์ผ์ด ์ค์ ํด์ค์ผํ๊ณ ,
๋์ ๋ฐ๋ผ์ ๋ฐ์ดํฐ์ก์ธ์ค ๊ณ์ธต์์ ์์กดํ๋ ์ค์ /์์กด ๊ฐ์ฒด๋ค๋ ๋ชจ๋ ์ค์ ํด ์ฃผ์ด์ผ ํ ์๋ ์๋ ๋ถํธํจ ์์
โ
โ@SpringBootTest
,@AutoConfigureMockMvc
๋ฅผ ์ด์ฉํด์ Controller ํ ์คํธ๋ฅผ ์ํ ๊ตฌ์ฑ์ ๋ณต์กํจ์ ํด๊ฒฐ
โ
โ But, ๊ฐ๊ฐ์ ์ฅ๋จ์ ์ด ์กด์ฌํ๋ฏ๋ก ์ํฉ์ ๋ง๊ฒ ์ ์ ํ๊ฒ ์ฌ์ฉ !
์์ฒญ์ํ
โ perform()
Http Method, request URL, path variable์ ์ํ API ๋ฉ์๋
โ MockMvcRequestBuilders.post(...)
, MockMvcRequestBuilders.patch(...)
, MockMvcRequestBuilders.get(...)
, MockMvcRequestBuilders.delete(...)
โ
โ MockMvcRequestBuilders
- GET, POST, PUT, DELETE ์์ฒญ ๋ฐฉ์๊ณผ ๋งคํ๋๋ get(), post(), put(), delete() ๋ฉ์๋๋ฅผ ์ ๊ณต
request body๋ฅผ ์ํ API ๋ฉ์๋
โ content(...)
query parameter
โ params(...)
response body ๊ฒ์ฆ
โ andExpect(jsonPath("$.data.~~").value(...))
[์ฐธ๊ณ ]
https://www.baeldung.com/guide-to-jayway-jsonpath
https://scshim.tistory.com/321
( ์ค์ต์์ ํ์ฌ ๋ฐ์ดํฐ ์์ธ์ค ๊ณ์ธต์์ ์ฌ์ฉํ๊ณ ์๋ ๊ธฐ์ - Spring Data JPA )
โ DB์ ์ํ๋ฅผ ํ
์คํธ ์ผ์ด์ค ์คํ ์ด์ ์ผ๋ก ๋๋๋ ค์ ๊นจ๋ํ๊ฒ ๋ง๋ค์ด์ผํจ
โ ๊ฐ๊ฐ์ ํ
์คํธ ์ผ์ด์ค์ ๋
๋ฆฝ์ฑ์ด ๋ณด์ฅ๋์ด์ผ ํจ
Ex. DB์ ์์์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ ๊ทธ๊ฒ์ด ์ ์กฐํ๋๋์ง ํ
์คํธ๋ฅผ ํ์์ผ๋ฉด,
๊ทธ ์ ์ฅํ ๊ฐ์ ๋ค์ ์ญ์ ํด์ฃผ์ด DB๋ฅผ ์๋๋๋ก ๋ณด์กด์์ผ๋์์ผ ํจ
โ๏ธ Repository ํ ์คํธ๋ฅผ ์ํ ํ ์คํธ ํด๋์ค ๊ตฌ์กฐ
@DataJpaTest public class RepositoryTestDefaultStructure { โ @Autowired private MemberRepository memberRepository; โ @Test public void saveMemberTest() { // ํ ์คํธํ๊ณ ์ ํ๋ Controller ํธ๋ค๋ฌ ๋ฉ์๋์ ํ ์คํธ ์ผ์ด์ค ์์ฑ โ // given // ํ ์คํธ์ฉ ๋ฐ์ดํฐ ์ค๋น ( ๊ฐ์ฒด์ ๋ฐ์ดํฐ ์ ์ฅ ) โ // when // Repository์ ๊ฐ์ฒด ์ ์ฅ // ์ ๋ณด๋ค ์ค ํ๋๋ง ๋ฝ์ ๊ฒ์ฆํ๊ณ ์ถ๋ค๋ฉด Repository์ findByXXX() ๋ฉ์๋ ์ฌ์ฉ โ // then // ์ ๋ณด๊ฐ ์ ์ ์ฅ๋์๋์ง ๊ฒ์ฆ (Assertion) } }
@DataJpaTest
๋ฐ์ดํฐ ์ก์ธ์ค ๊ณ์ธต์ ํ ์คํธ ํ๊ธฐ ์ํ ๊ฐ์ฅ ํต์ฌ์ ์ธ ๋ฐฉ๋ฒ
ํด๋น Repository์ ๊ธฐ๋ฅ์ ์ ์์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ Configuration์ Spring์ด ์๋์ผ๋ก ํด์ค
( ๋ฐ์ดํฐ ์ก์ธ์ค ๊ณ์ธต์ ํ์ํ ์๋๊ตฌ์ฑ ํ์ฑํ )
@Transactional
์ ๋ํ
์ด์
์ ํฌํจํ๊ณ ์์
@Transactional
........................................................................................................................................................................................................
๐ก ๊ฒฐ๊ณผ๋ฅผ ์ฝ์์ฐฝ์์ ํ์ธํ ๋ response body ์๋ต ๋ฐ์ดํฐ์ ํฌํจ๋ ํ๊ธ์ด ๊นจ์ง ๊ฒฝ์ฐ,
application.yml
ํ์ผ์ ์๋์ ์ค์ ์ ์ถ๊ฐ
server: servlet: encoding: force-response: true
........................................................................................................................................................................................................
But, Controller ํ
์คํธ์์ ์ค๋ ์์ฑํ ์ค์ต ๋ด์ฉ์ Controller๋ง ํ
์คํธํ๋ ๊ฒ์ด ์๋๋ผ ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ฒด ๋ก์ง์ ๋ชจ๋ ์คํํ๊ฒ๋จ
โ ์์ ํ ์ฌ๋ผ์ด์ค ํ ์คํธ๋ผ๊ณ ๋ณด๊ธฐ์๋ ํ๋ฆ
โ ์ด ๋ฌธ์ ๋ Mock(๊ฐ์ง) ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๊ณ์ธต ๊ฐ์ ์ฐ๊ฒฐ์ ๋์ด์ค์ผ๋ก์จ ํด๊ฒฐ์ด ๊ฐ๋ฅ
ํ
์คํ
ํ๋ ๋ถ๋ถ ํ์ต์ ์ฌ๋ฐ๋ ๊ฒ ๊ฐ๋ค !
ํ
์คํ
์ด ์ ๋์ ๋ โ๏ธ ์ด ํ์๊ฐ ๋์ค๋ ๊ฒ ๋ญ๊ฐ ๋ฟ๋ฏ ..!
์ค์ต์ ๋ค ๊ด์ฐฎ์๋๋ฐ getMembers ํ
์คํ
๋ถ๋ถ์ด ์ด๋ ค์ ๋ค. ( ์ด๊ฑด ๋ค์ ํ๋ฒ ๋ ๋ณด๊ธฐ ! )