๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BackEnd๐ŸŒฑ/Spring

AssertJ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์•Œ์•„๋ณด๊ธฐ

by ์•ˆ์ฃผํ˜• 2022. 8. 8.

AssertJ๋ž€?

โญ๏ธ ํ…Œ์ŠคํŠธ์— ๊ด€๋ จ๋œ ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์œผ๋กœ ๊ฐ€๋…์„ฑ ๋†’์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์„ ์ง€์›ํ•˜๋Š” ์˜คํ”ˆ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
โญ๏ธ assertThat(๊ฒ€์ฆ๋Œ€์ƒ)๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์„ ์ด์šฉํ•˜์—ฌ ๊ฒ€์ฆ ๋ฉ”์„œ๋“œ๋ฅผ ์—ฐ์‡„์ ์œผ๋กœ ๋” ๊น”๋”ํ•˜๊ณ  ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


 

ํ•™์Šต ํ™˜๊ฒฝ

plugins {
	id 'org.springframework.boot' version '2.7.2'
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.bo	ot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

 

1. Test Fail Message

@Test
public void test1() {
    String str = "๊ฐ€์œ„";
    assertThat(str).as("๊ธฐ๋Œ€๊ฐ’ {๋ฐ”์œ„}์™€ ํ˜„์žฌ๊ฐ’ {%s}๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", str)
            .isEqualTo("๋ฐ”์œ„");
}

โญ๏ธ as(String description, Object... args)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
โญ๏ธ as๋Š” ๊ฒ€์ฆ ๋ฌธ๋ณด๋‹ค ์•ž์— ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฉฐ, ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒฝ์šฐ ๊ฒ€์ฆ ๋ฌธ ์ดํ›„ ํ˜ธ์ถœ์ด ์ค‘๋‹จ๋จ์œผ๋กœ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.


 

2. Collection Filtering๊ณผ Assertions

import org.assertj.core.util.Lists;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.not;
import static org.assertj.core.api.AssertionsForClassTypes.in;
import static org.assertj.core.api.AssertionsForClassTypes.notIn;


@SpringBootTest
class MemberTest {
    public Member member1, member2, member3;
    public List<Member> members;

    @BeforeEach
    public void createMember() {
        member1 = new Member("Kim", 20, MemberRole.ADMIN);
        member2 = new Member("Ahn", 20, MemberRole.BASIC);
        member3 = new Member("Park", 21, MemberRole.VIP);
        members = Lists.list(member1, member2, member3);
    }


    /**
     * ๋ฆฌ์ŠคํŠธ ์ค‘ ๋‚˜์ด๊ฐ€ 20์‚ด์ธ ๋ฉค๋ฒ„๋“ค์€ member1๊ณผ member2๋งŒ์ด ์œ ์ผํ•˜๋‹ค.
     */
    @Test
    public void test1() {
        assertThat(members)
                .filteredOn("age", 20)
                .containsOnly(member1, member2);
    }

    /**
     * ๋ฆฌ์ŠคํŠธ ์ค‘ ๋‚˜์ด 19,20๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์€ ๋ฉค๋ฒ„๋Š” member3์ด ์œ ์ผํ•˜๋‹ค.
     */
    @Test
    public void test2() {
        assertThat(members)
                .filteredOn("age", notIn(19, 20))
                .containsOnly(member3);
    }

    /**
     * ๋ฆฌ์ŠคํŠธ ์ค‘ ๋‚˜์ด๊ฐ€ 20์ธ ์‚ฌ๋žŒ์€ member1๊ณผ member2๊ฐ€ ์œ ์ผํ•˜๋‹ค.
     */
    @Test
    public void test3() {
        assertThat(members)
                .filteredOn("age", in(20))
                .containsOnly(member1, member2);
    }

    /**
     * ๋ฆฌ์ŠคํŠธ ์ค‘ ์ด๋ฆ„์ด Lee๊ฐ€ ์•„๋‹Œ ๋ฉค๋ฒ„๋“ค์€ member1, member2, member3์ด ์œ ์ผํ•˜๋‹ค.
     */
    @Test
    public void test4() {
        assertThat(members)
                .filteredOn("name", not("Lee"))
                .containsOnly(member1, member2, member3);
    }

    /**
     * ์ฒด์ด๋‹์œผ๋กœ ํ•„ํ„ฐ๋ง์„ ์ด์–ด๊ฐˆ ์ˆ˜๋„ ์žˆ๋‹ค.
     */
    @Test
    public void test5() {
        assertThat(members)
                .filteredOn("age", 21)
                .filteredOn("name", not("Park"))
                .isEmpty();
    }
    
     /**
     * ๋žŒ๋‹ค์‹์œผ๋กœ ์กฐ๊ฑด๋„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
     */
    @Test
    public void test6() {
        assertThat(members)
                .filteredOn("age", 20)
                .filteredOn(member -> member.age > 20)
                .isEmpty();
    }
    
}

 

3. Collection Data Extracting

 /**
 * members์˜ name์„ ํ…Œ์ŠคํŠธํ•  ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฒˆ๊ฑฐ๋กœ์šด ๋ฐฉ์‹์œผ๋กœ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
 */
@Test
public void test7() {
    List<String> names = new ArrayList<>();
    for (Member member : members) {
        names.add(member.getName());
    }
    assertThat(names).containsOnly("Kim", "Ahn", "Park");
}

/**
 * ํ…Œ์ŠคํŠธ 7๋ฒˆ์„ ๊ฐœ์„ ํ•œ ์ฝ”๋“œ
 * extracting์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.
 */
@Test
public void test8() {
    assertThat(members)
            .extracting("name")
            .containsOnly("Kim", "Ahn", "Park");
}

/**
 * members์˜ member ์ด๋ฆ„๋“ค์€ Kim๊ณผ Ahn์„ ํฌํ•จํ•˜๊ณ  Lee๋Š” ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค.
 */
@Test
public void test8_1() {
    assertThat(members)
            .extracting("name")
            .contains("Kim", "Ahn")
            .doesNotContain("Lee");
}

 

4. Soft Assertions

โญ๏ธ ๋ณดํ†ต ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ ์•ž์˜ ๊ฒ€์ฆ์ด ์‹คํŒจํ•˜๋ฉด ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ์ „๋ถ€ ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค.

@SpringBootTest
class MemberTest { 
    public Member member1, member2, member3;
    public List<Member> members;

    @BeforeEach
    public void createMember() {
        member1 = new Member("Kim", 20, MemberRole.ADMIN);
        member2 = new Member("Ahn", 20, MemberRole.BASIC);
        member3 = new Member("Park", 21, MemberRole.VIP);
        members = Lists.list(member1, member2, member3);
    }

	
     /**
     * ์ฒซ ๋ฒˆ์งธ assert์—์„œ ์‹คํŒจํ•˜์—ฌ ๋‹ค์Œ assert๋Š” ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.
     */
    @Test
    public void noSoftAssertionTest() {
        assertThat(members).as("ํฌ๊ธฐ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.").size().isEqualTo(1);
        assertThat(members).as("์œ„ ๊ฒ€์ฆ์—์„œ ๋ฉˆ์ถฐ ์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.").size().isEqualTo(2);
    }
}

์œ„ ์ฝ”๋“œ๋Š” ์ฒซ ๋ฒˆ์งธ assert์—์„œ ์‹คํŒจํ•˜์—ฌ ์•„๋ž˜์˜ ๋‘ ๋ฒˆ์งธ assert๋Š” ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
โญ๏ธ ์•„๋ž˜์™€ ๊ฐ™์ด Soft Assertions์„ ์ด์šฉํ•˜๋ฉด ์ด์ „์˜ ์‹คํŒจ์™€ ์ƒ๊ด€์—†์ด ๋ชจ๋“  asserThat๋ฅผ ์‹คํ–‰ํ•œ ๋’ค ์‹คํŒจ ๋‚ด์—ญ์„ ์ „๋ถ€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@SpringBootTest
class MemberTest { 
    public Member member1, member2, member3;
    public List<Member> members;

    @BeforeEach
    public void createMember() {
        member1 = new Member("Kim", 20, MemberRole.ADMIN);
        member2 = new Member("Ahn", 20, MemberRole.BASIC);
        member3 = new Member("Park", 21, MemberRole.VIP);
        members = Lists.list(member1, member2, member3);
    }

     /**
     * ์ฒซ ๋ฒˆ์งธ assert๊ฐ€ ์‹คํŒจํ•˜๋”๋ผ๋„ ๋ชจ๋“  assert๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
     */
    @Test
    public void softAssertionTest() {
        SoftAssertions softAssertions = new SoftAssertions();
        softAssertions.assertThat(members).as("ํฌ๊ธฐ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.").size().isEqualTo(1);
        softAssertions.assertThat(members).as("์œ„ ๊ฒ€์ฆ์—์„œ ์‹คํŒจํ•˜๋”๋ผ๋„ ๋ชจ๋“  assert๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.").size().isEqualTo(2);
        softAssertions.assertAll(); // assertAll()์„ ํ†ตํ•ด ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    }
}


โญ๏ธ ์•„๋ž˜์™€ ๊ฐ™์ด assertSoftly๋ฅผ ์‚ฌ์šฉํ•ด ๋žŒ๋‹ค์‹์œผ๋กœ ๋”ฐ๋กœ assertAll์„ ํ˜ธ์ถœํ•  ํ•„์š” ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

@SpringBootTest
class MemberTest { 
    public Member member1, member2, member3;
    public List<Member> members;

    @BeforeEach
    public void createMember() {
        member1 = new Member("Kim", 20, MemberRole.ADMIN);
        member2 = new Member("Ahn", 20, MemberRole.BASIC);
        member3 = new Member("Park", 21, MemberRole.VIP);
        members = Lists.list(member1, member2, member3);
    }


    /**
     * assertSoftly๋ฅผ ์‚ฌ์šฉํ•ด ๋žŒ๋‹ค์‹์œผ๋กœ ๋”ฐ๋กœ assertAll์„ ํ˜ธ์ถœํ•  ํ•„์š” ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
     */
    @Test
    public void softAssertionTest_2() {
        SoftAssertions.assertSoftly(softAssertions -> {
            softAssertions.assertThat(members).as("ํฌ๊ธฐ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.").size().isEqualTo(1);
            softAssertions.assertThat(members).as("์œ„ ๊ฒ€์ฆ์—์„œ ์‹คํŒจํ•˜๋”๋ผ๋„ ๋ชจ๋“  assert๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.").size().isEqualTo(2);
        });
    }
}

 

5. Exception Assertions

โญ๏ธ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , ์˜ˆ์ƒํ•œ ์˜ˆ์™ธ์ธ์ง€ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Java 8์—์„œ Assertion์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์€ ํŽธ๋ฆฌํ•œ๋ฐ, assertThatThrownBy(ThrowingCallable)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ€๋…์„ฑ ์žˆ๊ณ  ํŽธ๋ฆฌํ•˜๊ฒŒ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ณ  ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

    /**
     * ์˜ˆ์™ธ ๋ฐœ์ƒ!์ด๋ผ๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ณ , hasMessageContaining์—์„œ ํ•ด๋‹น ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€์™€ ์ผ์น˜ํ•œ์ง€ ๊ฒ€์ฆํ•œ๋‹ค.
     */
    @Test
    public void testException_01() {
        assertThatThrownBy(() -> {
            throw new Exception("์˜ˆ์™ธ ๋ฐœ์ƒ!");
        })
                .isInstanceOf(Exception.class)
                .hasMessageContaining("์˜ˆ์™ธ ๋ฐœ์ƒ!");
    }

โญ๏ธ AssertJ์—์„œ๋Š” ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋“ค์— ๋Œ€ํ•ด์„œ ์ •์˜๋œ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ. ๋Œ€ํ‘œ์ ์œผ๋กœ 4๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

1. assertThatNullPointerException
2. assertThatIllegalArgumentException
3. assertThatIllegalStateException
4. assertThatIOException

โญ๏ธ ๋งŒ์•ฝ ์œ„์˜ ๋Œ€ํ‘œ์ ์ธ ๋„ค ๊ฐ€์ง€ ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    /**
     * ๋Œ€ํ‘œ์ ์ธ 4๊ฐ€์ง€ ์˜ˆ์™ธ์— ํฌํ•จ ๋˜์—ˆ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
     */
    @Test
    public void testException_02() {
        assertThatIOException().isThrownBy(() -> {
                    throw new IOException("์˜ˆ์™ธ ๋ฐœ์ƒ!");
                })
                .withMessage("%s!", "์˜ˆ์™ธ ๋ฐœ์ƒ!")
                .withMessageContaining("์˜ˆ์™ธ ๋ฐœ์ƒ!")
                .withNoCause();
    }

โญ๏ธ ๋งŒ์•ฝ ์œ„ ๋„ค ๊ฐ€์ง€ ์˜ˆ์™ธ์— ํฌํ•จ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    /**
     * ๋Œ€ํ‘œ์ ์ธ 4๊ฐ€์ง€ ์˜ˆ์™ธ์— ํฌํ•จ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.
     */
    @Test
    public void testException_03() {
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> {
                    throw new IOException("์˜ˆ์™ธ ๋ฐœ์ƒ!");
                })
                .withMessage("%s", "์˜ˆ์™ธ ๋ฐœ์ƒ!")
                .withMessageContaining("์˜ˆ์™ธ ๋ฐœ์ƒ!")
                .withNoCause();
    }

โญ๏ธ BDD(ํ–‰๋™ ์ฃผ๋„ ๊ฐœ๋ฐœ) ๋ฐฉ์‹์œผ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 /**
     * BDD(ํ–‰๋™ ์ฃผ๋„ ๊ฐœ๋ฐœ) ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
     */
    @Test
    public void testException_04() {
        //given

        //when
        Throwable thrown = catchThrowable(() -> {
            throw new Exception("์˜ˆ์™ธ ๋˜์ง€๊ธฐ!");
        });

        //then
        assertThat(thrown)
                .isInstanceOf(Exception.class)
                .hasMessageContaining("์˜ˆ์™ธ ๋˜์ง€๊ธฐ!");
    }

 

์ฐธ๊ณ 

 

ํ…Œ์ŠคํŠธ ๊ด€๋ จ ๊ฐ™์ด ๋ณด๋ฉด ์ข‹์€ ๊ธ€

๋Œ“๊ธ€