์˜ฌ๋ฆฌ๋ธŒ์˜ ํ…Œํฌ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ… TestFixture๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค?

TestFixture๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค?

๐Ÿ™‰FixtureMonkey๋กœ TestFixture๋ฅผ ๋งŒ๋“ค๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ

2024.04.01

Fixture๋ฅผ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฐ๋ฐ ์ด์ œ ๐Ÿ™‰ FixtureMonkey๋ฅผ ๊ณ๋“ค์ธ...

์•ˆ๋…•ํ•˜์„ธ์š”. ๐Ÿ˜€
์˜ฌ๋ฆฌ๋ธŒ์˜์—์„œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๊ณ  ์žˆ๋Š” ์œค๋…ธํŠธ์ž…๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ• ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Fixture์— ๋Œ€ํ•œ ์„ค๋ช…๊ณผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•œ ์ด์Šˆ๋“ค ๊ทธ๋ฆฌ๊ณ  FixtureMonkey๋กœ ์ด๋ฅผ ํ•ด๊ฒฐํ•œ ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ› ๏ธ Fixture

Fixture๋ฅผ ๋‹จ์–ด ๊ทธ๋Œ€๋กœ ํ•ด์„ํ•˜๋ฉด ๊ณ ์ •๋˜์–ด ์žˆ๋Š” ๋ฌผ์ฒด๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์—์„œ Fixture ๋ž€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํŠน์ • ๊ฐ์ฒด์— ๋Œ€ํ•ด ์‚ฌ์ „์— ๋ฏธ๋ฆฌ ์ •์˜ํ•˜์—ฌ ํ•„์š”์‹œ ์ •์˜๋œ ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์šฉ๋„
๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋ฅผ Fixture๋กœ ์ •์˜ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ์šฉ๋„


๐Ÿค” Fixture ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ?

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ํ•œ.๋ฒˆ.์ฏค์€ "์–ด๋–ป๊ฒŒ ํ…Œ์ŠคํŠธ์šฉ ๊ฐ์ฒด๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ํ• ์ง€?" "์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ์žฌ์‚ฌ์šฉ ํ•˜๊ธฐ ์ข‹์€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ• ์ง€" ๊ณ ๋ฏผํ•ด ๋ณด์…จ์„ ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ณ ๋ฏผ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ณดํ†ต ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ์‹์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹
  • ํŒจํ„ด์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹
  • JSON ํŒŒ์ผ๋กœ ๋งŒ๋“ค์–ด๋†“๊ณ  ObjectMapper๋ฅผ ํ†ตํ•ด ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐฉ์‹

์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ์‹์ค‘ ํŒŒํŠธ๋„ˆ์˜คํ”ผ์Šค ์Šค์ฟผ๋“œ ์—์„œ๋Š” Test Data Builder ํŒจํ„ด๊ณผ Object Mother ํŒจํ„ด ๋“ฑ์˜ ํŒจํ„ด์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹๊ณผ, JSON ํŒŒ์ผ์„ Object๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์„ ํ™œ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.



๐Ÿ› ๏ธ ์ง์ ‘ ์ƒ์„ฑ์ž๋กœ Fixture ์ƒ์„ฑํ•˜๊ธฐ

๋ง ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ…Œ์ŠคํŠธ ์‹œ์ ์— ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, @BeforeAll ๋˜๋Š” @BeforeEach๋ฅผ ํ†ตํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ์ „์— ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.


public class SampleTest {

    private static User user;

    @BeforeAll
    static void setup() {
        user = new User(/* name */ "์œค๋…ธํŠธ",  /* age */ 32,  /* intro */"๐Ÿง‘โ€๐Ÿ’ป");
    }

    @Test
    void sampleTest() {

        final String expectName = "์œค๋…ธํŠธ";
        final int age = 32;
        final String intro = "๐Ÿง‘โ€๐Ÿ’ป";
        final User actual = UserFixture.createUser();

        assertThat(actual.getName()).isEqualTo(expectName);
        assertThat(actual.getAge()).isEqualTo(age);
        assertThat(actual.getIntro()).isEqualTo(intro);
    }
}

๐Ÿ› ๏ธ Test Data Builder ํŒจํ„ด๊ณผ Object Mother ํŒจํ„ด์œผ๋กœ Fixture ์ƒ์„ฑํ•˜๊ธฐ

Builder๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์—ฌ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜, Lombok์—์„œ ์ œ๊ณตํ•˜๋Š” @Builder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•˜๋ฉด ํ•„์š”ํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ํŽธ์˜ ๋ฉ”์„œ๋“œ๋กœ ์ง์ ‘ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Builder๋ฅผ ์‚ฌ์šฉํ•œ User ์ฝ”๋“œ

/** User.class */
@Getter
@Builder
public class User {

    private String name;
    private int age;
    private String intro;

    private User (UserBuilder userBuilder) {
        this.name = userBuilder.name;
        this.age = userBuilder.age;
        this.intro = userBuilder.intro;
    }
}

์ง์ ‘ ๊ตฌํ˜„ํ•œ Builder๋ฅผ ์‚ฌ์šฉํ•˜๋Š” User ์ฝ”๋“œ

/** User.class */
@Getter
public class User {

    // ํ•„๋“œ ์ƒ๋žต
    //...

    private User (UserBuilder userBuilder) {
        //...
    }

    public static class UserBuilder {

        // ํ•„๋“œ ์ƒ๋žต
        //...

        // ๋ฉ”์„œ๋“œ ์ƒ๋žต
        // ...

        public User build() {
            return new User(this);
        }
    }
}

UserFixture ์ฝ”๋“œ

public class UserFixture {
    public static User createUser() {
        // ์ง์ ‘ ์ž‘์„ฑํ•œ Builder
        return new User.UserBuilder()
                .name("์œค๋…ธํŠธ")
                .age(32)
                .intro("๐Ÿง‘โ€๐Ÿ’ป")
                .build();
    }

    public static User createUserBuilderType() {
        // Lombok์„ ์‚ฌ์šฉํ•œ Builder
        return User.builder()
                .name("์œค๋…ธํŠธ")
                .age(32)
                .intro("๐Ÿง‘โ€๐Ÿ’ป")
                .build();
    }
}

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ


@DisplayName("[User] ์ง์ ‘ ๊ตฌํ˜„ํ•œ Builder Test")
@Test
void builderTypeTest() {

    final String expectName = "์œค๋…ธํŠธ";
    final int age = 32;
    final String intro = "๐Ÿง‘โ€๐Ÿ’ป";

    // ์ง์ ‘ ๊ตฌํ˜„ํ•œ Builder
    final User actual = UserFixture.createUser();

    assertThat(actual.getName()).isEqualTo(expectName);
    assertThat(actual.getAge()).isEqualTo(age);
    assertThat(actual.getIntro()).isEqualTo(intro);
}

@DisplayName("[User] Lombok Builder Test")
@Test
void lombokBuilderTypeTest () {

    final String expectName = "์œค๋…ธํŠธ";
    final int age = 32;
    final String intro = "๐Ÿง‘โ€๐Ÿ’ป";

    // Lombok์„ ํ†ตํ•ด ๊ตฌํ˜„
    final User actual = UserFixture.createUser();

    // .. ๊ฒ€์ฆ ์ƒ๋žต ์œ„์™€ ๋™์ผ
}


๐Ÿค” ๊ธฐ์กด ๋ฐฉ์‹์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ

์œ„์ฒ˜๋Ÿผ Fixture๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์œ ์—ฐ์„ฑ ๋ถ€์กฑ ๋ฐ ์ฝ”๋“œ ๋™๊ธฐํ™” ์–ด๋ ค์›€

    ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ํ›„ API์˜ ์ˆ˜์ •์ด ์žˆ์„ ๊ฒฝ์šฐ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ํ•จ๊ป˜ ์ˆ˜์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
    ๋˜ํ•œ ๋น ๋ฅด๊ฒŒ API๋ฅผ ์ˆ˜์ •ํ•˜๋‹ค ๋ณด๋‹ˆ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ๋ชปํ•˜์—ฌ ๋™๊ธฐํ™” ๋˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  2. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ปค๋ฒ„ํ•˜์ง€ ๋ชปํ•˜๋Š” ์—ฃ์ง€ ์ผ€์ด์Šค ๋ฐœ์ƒ

    ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋ฏธ๋ฆฌ ์ž‘์„ฑํ•˜๊ณ  API๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์—ฌ๋Ÿฌ ์˜ˆ์™ธ ์ƒํ™ฉ์„ ๋ฏธ๋ฆฌ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ๋ ‡์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ์—๋Š” API๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•œ ํ›„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋‹ˆ ์ด๋ฏธ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์•ผ ์—๋Ÿฌ๊ฐ€ ์•ˆ ๋‚˜๋Š”์ง€ ํ•™์Šตํ•ด๋ฒ„๋ ค์„œ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ๋†“์น˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  3. ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•ด ์˜คํžˆ๋ ค ๋ณต์žกํ•ด์ง€๋Š” ์ฝ”๋“œ

    ๋ณต์žกํ•œ ๋กœ์ง ํ•˜๋‚˜๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋” ๋ณต์žกํ•ด์ง€๋Š” ์ฝ”๋“œ๊ฐ€ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.
    ๋˜ํ•œ ๋‹ค์–‘ํ•œ ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” JSON ํŒŒ์ผ๋“ค์„ ์ผ€์ด์Šค๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•˜๋ฉฐ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐ์— ๋ถˆํŽธํ•จ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


img
(์กฐ๋„ˆ์„  ํด ์•„์ด๋ธŒ์˜ ์œ ๋ช…ํ•œ ๊ฒฉ์–ธ์— sudo rm -rf ๋ฅผ ์–น์–ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.)

ํŒŒํŠธ๋„ˆ์˜คํ”ผ์Šค ์Šค์ฟผ๋“œ์—์„œ๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Fixture Monkey๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.



๊ธฐ์กด Fixture๋ฅผ ๋ฐ”๊ฟ”๋ณด์ž! ๐Ÿ™‰ Fixture Monkey ๋“ฑ์žฅ!

FixtureMonkey ๋Œ€๋ฌธ FixtureMonkey ํŠน์ง•
(์ด๋ฏธ์ง€ ์ถœ์ฒ˜ : Fixture Monkey ๊ณต์‹ ์‚ฌ์ดํŠธ)

Fixture Monkey๋Š” 2023.11.10์ผ์— ์ •์‹ 1.0.0 ๋ฒ„์ „์ด Release ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Java & Kotlin library for automatically generating reusable and controllable, arbitrary test fixtures Fixture Monkey์˜ ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€ ๋Œ€๋ฌธ์— ๊ฑธ๋ ค์žˆ๋Š” ๊ธ€์ž…๋‹ˆ๋‹ค.

์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ณ  ๋ณต์žกํ•œ ์ž„์˜์˜ ํ…Œ์ŠคํŠธ Fixture๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด ์ฃผ๋Š” Java&Kotlin ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ž๋ฐ” ํ‘œ์ค€ Bean Validation 1.0(JSR-303), Bean Validation 2.0 (JSR-380) ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์šฉ ์–ด๋…ธํ…Œ์ด์…˜์ด ์ถ”๊ฐ€๋กœ ํ•„์š”ํ•˜์ง€๊ฐ€ ์•Š๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.




โœ๏ธ ํŽธํ•œ ๊ฑด ์•Œ์•˜์œผ๋‹ˆ ์ด์ œ ์‚ฌ์šฉํ•ด๋ณด์ž!

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ๋ฐฉ๋ฒ•์€ Github์— ๋„ˆ๋ฌด ์ž˜ ๋‚˜์™€์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ ์šฉํ•˜์˜€๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ์ฝ”๋“œ์— FixtureMonkey.create()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Test
void FixtureMonkeySample() {
    final int MAX_SIZE = 5;
    FixtureMonkey fixtureMonkey = FixtureMonkey.create();

    List<User> users = fixtureMonkey.giveMe(User.class, MAX_SIZE);

    assertThat(users).hasSize(MAX_SIZE);
}

img_2.png


์œ„ ์‚ฌ์ง„์€ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ๋””๋ฒ„๊น… ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜์˜€์„ ๋•Œ ๋ฐ์ดํ„ฐ ๊ฒฐ๊ณผ๊ฐ’์ž…๋‹ˆ๋‹ค.

users ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด ๋žœ๋คํ•˜๊ฒŒ ์ƒ์„ฑ๋œ size 5์˜ ์ปฌ๋ ‰์…˜์„ ๋ฐ˜ํ™˜ํ•œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ฐ๊ฐ index์— ํ•ด๋‹นํ•˜๋Š” row์— Fixture Monkey๊ฐ€ ๋žœ๋คํ•œ ๊ฐ’๋“ค์„ ๋„ฃ์–ด์ค€ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


ํ•ด๋‹น ์ฝ”๋“œ๋Š” Builder๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋˜๋Š” ํŠน์ • Field์˜ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ๊ณ ์ •ํ•˜๊ฑฐ๋‚˜, Fixture Monkey์—์„œ ์ œ๊ณตํ•˜๋Š” Arbitraries๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ๋ฒ”์œ„์˜ ๊ฐ’๋งŒ ๋„ฃ๋„๋ก ์„ค์ •ํ•œ ์˜ˆ์ž…๋‹ˆ๋‹ค. age์˜ ๊ฒฝ์šฐ 10~100์‚ฌ์ด์˜ ๊ฐ’๋งŒ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.
์šฐ์ธก ์‚ฌ์ง„์ฒ˜๋Ÿผ ๊ฐ index์˜ ๋ฐ์ดํ„ฐ์˜ age๊ฐ€ 10 ์ด์ƒ 100 ์ดํ•˜์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Test
void FixtureMonkeySample() {
    final int MAX_SIZE = 5;
    FixtureMonkey fixtureMonkey = FixtureMonkey.create();

    List<User> users = fixtureMonkey.giveMeBuilder(User.class)
            .set("age" , Arbitraries.integers().between(10, 100))
            .sampleList(MAX_SIZE);

    assertThat(users).hasSize(MAX_SIZE);
    assertThat(actual.get(0).getAge()).isBetween(10, 100);
}

img_3.png

๐Ÿ”ฅ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด User.class๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋Œ๋ ธ๋”๋‹ˆ ์‹คํ–‰์ด ์•ˆ ๋˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š๋Š”๋‹ค๋ฉด Getter, Setter๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์‹œ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค! ํ•ด๋‹น ๋‚ด์šฉ์€ ์•„๋ž˜์—์„œ ํ•œ ๋ฒˆ ๋” ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.




โ“ ํŒŒํŠธ๋„ˆ์˜คํ”ผ์Šค ์Šค์ฟผ๋“œ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์ ์šฉํ•˜์˜€์„๊นŒ?

  1. ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ์ „๋žต์„ FailoverArbitraryIntrospector ์„ค์ •
  2. ๊ธฐ์กด Fixture ์ƒ์„ฑ ๋ฐฉ์‹์„ Fixture Monkey๋กœ ๋ณ€๊ฒฝ
  3. ์žฌ์‚ฌ์šฉโ™ป๏ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์€ Util๋กœ ๋ณ€๊ฒฝ

๋ฐ์ดํ„ฐ ๊ธฐ๋ณธ ์ƒ์„ฑ ์ „๋žต ๋ณ€๊ฒฝ

Fixture Monkey ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐฉ๋ฒ• Fixture Monkey์˜ ๊ธฐ๋ณธ ์ƒ์„ฑ ๋ฐฉ์‹์€ BeanArbitraryIntrospector ์ž…๋‹ˆ๋‹ค.


โญ BeanArbitraryIntrospector

BeanArbitraryIntrospector ๋ฐฉ์‹์€ ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ Setter ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ํด๋ž˜์Šค์— ๊ธฐ๋ณธ ์ƒ์„ฑ์ž์™€ Setter๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


โญ ConstructorPropertiesArbitraryIntrospector

ConstructorPropertiesArbitraryIntrospector ๋ฐฉ์‹์€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์ƒ์„ฑ์ž๋ฅผ ์ด์šฉํ•œ ์ƒ์„ฑ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.


โญ FieldReflectionArbitraryIntrospector

FieldReflectionArbitraryIntrospector๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ๋ฐฉ์‹์„ ์ด์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ•„๋“œ์— ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž์™€ Getter ๋˜๋Š” Setter๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ์„ค๋ช…์ด ๋˜์–ด์žˆ์ง€๋งŒ, ์‹ค์ œ ํ…Œ์ŠคํŠธ์‹œ Getter, Setter ์—†์ด ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋งŒ ์žˆ์–ด๋„ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋‹จ, final์ด ์•„๋‹Œ ๊ฐ€๋ณ€ ๊ฐ์ฒด์— ๋Œ€ํ•ด์„œ๋Š” @Getter, @Setter๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ๋„ ์ƒ์„ฑ๋œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค!
https://github.com/naver/fixture-monkey/issues/961#issuecomment-2021906200


โญ BuilderArbitraryIntrospector

BuilderArbitraryIntrospector๋Š” ๋นŒ๋” ๋ฐฉ์‹์„ ์ด์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ•„๋“œ์— ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
Lombok @Builder๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, Lombok์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ builder, build ์ด๋ฆ„์„ ๊ฐ–๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์„ค์ •๋˜์–ด ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

img_9.png

์ง์ ‘ Builder ๊ตฌํ˜„ํ•˜๋Š” ๊ฒฝ์šฐ
public class User{

    // ํ•„๋“œ ์ƒ๋žต
    // ...

    private User(Long id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public static class Builder {
        // ํ•„๋“œ ์ƒ๋žต
        // ...

        // ๋ฉ”์„œ๋“œ ์ƒ๋žต
        // ...

        // ํ•ด๋‹น ์ด๋ฆ„(build)์ด ์•„๋‹ˆ๋ผ๋ฉด ์ƒ์„ฑ๋˜์ง€ ์•Š์Œ.
        User build() {
            return new User(this.id, this.name, this.age);
        }
    }

    // ํ•ด๋‹น ์ด๋ฆ„(builder)์ด ์•„๋‹ˆ๋ผ๋ฉด ์ƒ์„ฑ๋˜์ง€ ์•Š์Œ.
    static Builder builder() {
        return new Builder();
    }
}

โญ FailoverArbitraryIntrospector

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋ฉด ์ž‘์„ฑ๋œ ์ฝ”๋“œ์˜ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐฉ์‹์ด ๋ชจ๋‘ ๋‹ฌ๋ผ ๋‹จ์ผ๋กœ๋Š” ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ FailoverArbitraryIntrospector๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ƒ์„ฑ ๋ฐฉ์‹์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.




๊ธฐ์กด Fixture ์ƒ์„ฑ ๋ฐฉ์‹์„ Fixture Monkey๋กœ ๋ณ€๊ฒฝ

/** ๊ธฐ์กด ์‚ฌ์šฉ ๋ฐฉ์‹ */
String fixtureResponse = readJson("ui/sample/api-response/fixture.json");
List<Sample.Response> contents = Arrays.asList(om.readValue(fixtureResponse, Sample.Response.class));

/** Fixture ์‚ฌ์šฉ ๋ฐฉ์‹ */
List<Sample.Response> contents = fixtureMonkey.giveMeBuilder(Sample.Response.class)
                                            .sampleList(size);

๊ธฐ์กด ํŒŒํŠธ๋„ˆ์˜คํ”ผ์Šค ์Šค์ฟผ๋“œ์— ์ž‘์„ฑ๋œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” test/resources ํ•˜์œ„์— ๋„๋ฉ”์ธ๋ณ„ fixture์— ํ•ด๋‹นํ•˜๋Š” JSON ํŒŒ์ผ๋“ค์„ ๊ตฌ์„ฑํ•˜๊ณ  ํŒŒ์ผ์„ ์ฝ์–ด ObjectMapper๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ JSON ํŒŒ์ผ์˜ ๊ฒฝ์šฐ ๋ณ€ํ™˜ํ•˜๊ณ ์ž ํ•˜๋Š” ํด๋ž˜์Šค์— ํ•„๋“œ๊ฐ€ ์—†์ง€๋งŒ JSON ํŒŒ์ผ์•ˆ์— ํ•ด๋‹น ํ‚ค๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ณ€ํ™˜ ์‹œ ObjectMapper์—์„œ UnrecognizedPropertyException์„ ๋ฐœ์ƒ์‹œ์ผœ JSON ํŒŒ์ผ์˜ ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ด๋‹น ๊ตฌ์กฐ๋ฅผ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ๋ธ ํด๋ž˜์Šค๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„ ์˜ˆ์ œ์ฒ˜๋Ÿผ Fixture Monkey๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋กœ Fixture์˜ ์ƒ์„ฑ, ์ •์ƒ ์ผ€์ด์Šค, ์—ฃ์ง€ ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ ์‰ฝ๊ฒŒ ์ƒ์„ฑ ๋ฐ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.




โ™ป๏ธ Utils ํด๋ž˜์Šค ๋ณ€๊ฒฝ ๋ฐ ์žฌ์‚ฌ์šฉ์„ฑ ๊ตฌ์„ฑ

Define complex specifications once and reuse them! Configurations of instances can be reused across multiple tests.

Fixture Monkey์˜ ์žฅ์  ์ค‘ ํ•˜๋‚˜์ธ Reusability์ž…๋‹ˆ๋‹ค. ๋ฉ”์ธ ํ™”๋ฉด์—์„œ ๊ฐ•์กฐํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณต์žกํ•œ ๊ตฌ์กฐ๋ฅผ ํ•œ๋ฒˆ ์ •๋ฆฌํ•˜์—ฌ ๋‹ค์ˆ˜์˜ ํ…Œ์ŠคํŠธ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋Š” Common(Util) ํด๋ž˜์Šค๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. Request, Response, Domain, Entity ์ฒ˜๋Ÿผ ์šฉ๋„์— ๋”ฐ๋ผ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— FixtureMonkey๊ฐ์ฒด๋ฅผ ์šฉ๋„์— ๋”ฐ๋ผ ๋ถ„๋ฆฌํ•˜๋Š”๊ฒƒ์ด ์•„๋‹Œ FailoverIntrospector์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑํ•˜๋„๋ก ๋ณ€๊ฒฝํ•˜์˜€์Šต๋‹ˆ๋‹ค.

public class FixtureCommon {

    /**
     * ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ์—์„œ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก static Field๋กœ ๊ตฌ์„ฑ
     * FixtureMonkeyUtils.fixtureMonkey;`์™€ ๊ฐ™์ด ๊ณตํ†ต ์„ค์ •์— ๋Œ€ํ•ด์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
     */
    public static FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
        .objectIntrospector(new FailoverIntrospector(
            Arrays.asList(
                FieldReflectionArbitraryIntrospector.INSTANCE,
                BeanArbitraryIntrospector.INSTANCE,
                BuilderArbitraryIntrospector.INSTANCE
            )
        ))

        // FixtureMonkey์˜ ๋Œ€์ž…๊ฐ’์— null ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ.
        .defaultNotNull(true)
        .build();
}

๋‘๋ฒˆ์งธ๋Š” ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋“ค์— ๋Œ€ํ•œ ์ •์˜ ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ์˜ˆ์ œ๋Š” ์‹ค์ œ ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ์ƒ˜ํ”Œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์ด๋ฆ„์€ ์œค๋…ธํŠธ์ธ๋ฐ ๋‚˜์ด๋งŒ 32์‚ด, 33์‚ด๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ๊ณผ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.
(์„ค๋ช…์„ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.)

public class FixtureCommon {
    // ...

    private static ArbitraryBuilder<User> fixName() {
        return fixtureMonkey.giveMeBuilder(User.class)
                .set("name", "์œค๋…ธํŠธ");
    }

    public static User age32YunNote() {
        return fixName()
                .set("age", 32)
                .sample();
    }

    public static User age33YunNote() {
        return fixName()
                .set("age", 33)
                .sample();
    }
}
@Test
void FixtureMonkeyReusabilityAge32Sample() {

    User actual = FixtureCommon.age32YunNote();

    assertThat(actual.getName()).isEqualTo("์œค๋…ธํŠธ");
    assertThat(actual.getAge()).isEqualTo(32);
}

@Test
void FixtureMonkeyReusabilityAge33Sample() {
    User actual = FixtureCommon.age33YunNote();

    assertThat(actual.getName()).isEqualTo("์œค๋…ธํŠธ");
    assertThat(actual.getAge()).isEqualTo(33);
}

์œ„์™€ ๊ฐ™์ด ArbitraryBuilder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑํ•˜๋Š” ๋ถ€๋ถ„์„ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ํ•„๋“œ๋“ค๋งŒ setํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.



๐Ÿ’ญ ์ •๋ฆฌ

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ถ„๋“ค์ด๋ผ๋ฉด ๐Ÿ™‰FixtureMonkey๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.
ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋ชจ๋‘ ์•Œ๊ณ  ์žˆ์ง€๋งŒ, ์—…๋ฌด๋ฅผ ๋ณด๋‹ค ๋ณด๋ฉด ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•˜๊ฑฐ๋‚˜, ํ…Œ์ŠคํŠธ ๊ฐ์ฒด๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ท€์ฐฎ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ์— ์ž‘์„ฑํ•ด์•ผ์ง€!! ๋ผ๊ณ  ๋„˜์–ด๊ฐ€๋Š” ๋ถ„๋“ค๋„ ๋งŽ์„ ๊ฒƒ์œผ๋กœ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
Fixture Monkey๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์ด ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋กœ ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ ์ƒ์„ฑํ•ด ๋‚ผ ์ˆ˜ ์žˆ์–ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์‹ ๋ขฐ๋„๊ฐ€ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ฝ”๋“œ์˜ ์–‘๋„ ๋งŽ์ง€ ์•Š์•„์„œ ๋” ๊น”๋”ํ•˜๊ณ  ๊ฐ€๋…์„ฑ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ํฐ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ํŒŒํŠธ๋„ˆ์˜คํ”ผ์Šค ์Šค์ฟผ๋“œ์—์„œ ์•ฝ 500๊ฐœ ์ด์ƒ์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ Fixture Monkey๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์„ฑํ–ˆ์œผ๋ฉฐ, ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ๊ด€์‹ฌ ํ•„๋“œ๋“ค์„ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜์—ฌ Fixture๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๊ณ , ์—ฃ์ง€ ์ผ€์ด์Šค๋“ค์„ ํ†ตํ•ด ๋ฏธ์ฒ˜ ๋ฐœ๊ฒฌํ•˜์ง€ ๋ชป ํ•œ ์ผ€์ด์Šค๋“ค์— ๋Œ€ํ•ด์„œ๋„ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.



๐Ÿ˜ Thanks to

  • Fixture Monkey ๋„์ž…์„ ์œ„ํ•ด ๊ฐ€์ด๋“œ ํ•ด์ฃผ์‹  Architect. ์ฝ”๋“œ๋‹ค์ด๋ฒ„๋‹˜ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.
  • ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉฐ ๊ฐ€์ด๋“œ๋ฅผ ์žก๋Š”๋ฐ ๋„์™€์ค€ ํŒ€, ์Šค์ฟผ๋“œ ๊ฐœ๋ฐœ์ž๋ถ„๋“ค๊ป˜ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

์ฐธ๊ณ 

FixtureFixtureMonkeyTestCode
์˜ฌ๋ฆฌ๋ธŒ์˜ ํ…Œํฌ ๋ธ”๋กœ๊ทธ ์ž‘์„ฑ TestFixture๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•ด ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค?
๐Ÿ’ป
์œค๋…ธํŠธ |
Back-end Engineer
๋”ฑ๋”ฑํ•จ์—์„œ ๋ฒ—์–ด๋‚˜ ์žฌ๋ฐŒ๋Š” ๊ธ€์„ ์“ฐ๊ณ  ์‹ถ์–ดํ•˜๋Š” Backend Engineer์ž…๋‹ˆ๋‹ค. ์žฌ๋ฏธ์—†์œผ๋ฉด ์—ฐ๋ฝ์ฃผ์„ธ์š”!