TDD(Test-driven Development) ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ๋ฐฉ๋ฒ•๋ก 

๊ฐ์ข… Community์—์„œ ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค๊ฐ€ TDD๊ฐ€ ์–ธ๊ธ‰์ด ๋˜์—ˆ๊ณ  ์˜ˆ์ „์— ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค ์ค‘์— ๋ฐฐ์› ๋˜ TDD์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ๋ชฐ๋ž๊ธฐ ๋•Œ๋ฌธ์— ๋ช…ํ™•ํ•˜๊ฒŒ ์ •์˜๋ฅผ ํ•˜๊ณ  ์•ž์œผ๋กœ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•์„ TDD๋กœ ๋ฐ”๊พธ๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ธฐ ์œ„ํ•ด Posting์„ ํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

1. TDD(Test-driven Development)์ด๋ž€?

  • ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค

    • ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐœ๋ฐœ ์ ˆ์ฐจ๋Š” ๋จผ์ € ์–ด๋–ป๊ฒŒ ๊ฐœ๋ฐœํ• ์ง€ ๋””์ž์ธํ•˜๊ณ  ๋””์ž์ธ์„ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ตœ์ข…์ ์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘๋™์‹œ์ผœ๋ณด๋ฉด์„œ ํ•ด๋ณด๋Š” ๊ณผ์ •์ด์—ˆ๋‹ค.

  • TDD(Test-driven Development)

    • ์ •ํ™•ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชฉ์ ์„ ๋””์ž์ธ ๋‹จ๊ณ„์—์„œ ๋ฐ˜๋“œ์‹œ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์•ผ๋งŒ ํ•˜๊ณ  ๋˜ ๋ฌด์—‡์„ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์•ผํ•œ๋‹ค.
    • RED : ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ.
    • GREEN : ํ…Œ์ŠคํŠธ์— ํ†ต๊ณผํ•  ๋งŒํ•œ ์ž‘์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ.
    • REFACTOR : ๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ, ๊ธด ๋ฉ”์†Œ๋“œ, ํฐ ํด๋ž˜์Šค, ๊ธด ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก ๋“ฑ๋“ฑ ์ฝ”๋“œ๋ฅผ ์ข€ ๋” ํšจ์œจ์ ์œผ๋กœ ๋ฐ”๊พธ๊ธฐ.

  • ๊ฐœ๋ฐœํ•˜๋Š” ๊ณผ์ •์—์„œ Test Script๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ Refactoring ํ–ˆ๋‹ค๋Š” ์ ์ด ์ค‘์š”ํ•˜๋‹ค.

2. TDD ๋”ฐ๋ผํ•˜๊ธฐ (JUnit)

๋ชฉํ‘œ : Movie๋ผ๋Š” ํด๋ž˜์Šค์— ๋“ฑ๊ธ‰์„ ๋ถ€์—ฌํ•˜๊ณ  Averaging Rate๊ตฌํ•˜๊ธฐ

1) JUnit์ด๋ž€?

  • ๋ฒ”์šฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ Framework
  • Java ์–ธ์–ด์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.
  • ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ JUnit Post https://nesoy.github.io/articles/2017-02/JUnit๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ๋œ๋‹ค.

2) pom.xml JUnit & Hamcrest ์ถ”๊ฐ€ ์ฝ”๋“œ

<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hamcrest</groupId>
			<artifactId>hamcrest-all</artifactId>
			<version>1.1</version>
			<scope>test</scope>
		</dependency>
</dependencies>

3) JUnit ์‹คํ–‰

package movie;
 
import org.junit.Test;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
    @Test
    public void foo(){
 
    }
}

Test ๊ฒฐ๊ณผํ™”๋ฉด

4) Failing Test ์ฝ”๋“œ ์ž‘์„ฑ

/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
    @Test
    public void canCreateMovie(){
        Movie movie = new Movie();
    }
}

5) Test๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์†Œ๋Ÿ‰์˜ ์ฝ”๋“œ ์ถ”๊ฐ€

package movie;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class Movie {
}

Test ๊ฒฐ๊ณผํ™”๋ฉด

6) Averaging Rating Test Case๋งŒ๋“ค๊ธฐ - ๊ฐ€์žฅ ์ดˆ๊ธฐ ๋ชจ๋ธ์ด๋ฏ€๋กœ Rate์ ์ˆ˜๊ฐ€ ์—†๋‹ค. โ†’ 0์ .

package movie;
 
import org.junit.Test;
 
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
    @Test
    public void canCreateMovie(){
        Movie movie = new Movie();
        assertThat(movie.averageRationg(),is(0));
    }
}

7) Test๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์†Œ๋Ÿ‰์˜ ์ฝ”๋“œ ์ถ”๊ฐ€

package movie;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class Movie {
    public Integer averageRationg() {
        return 0;
    }
}

Test ๊ฒฐ๊ณผํ™”๋ฉด

8) Refactoringํ•˜๊ธฐ - Method ์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ

package movie;
 
import org.junit.Test;
 
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
    @Test
    public void should_return_0_when_just_created(){
        Movie movie = new Movie();
        assertThat(movie.averageRationg(),is(0));
    }
}

9) Averaging Rating Test Case๋งŒ๋“ค๊ธฐ โ†’ 1์„ ์ฃผ์—ˆ์„๋•Œ Average Rate๊ฐ€ 1์ด ๋‚˜์™€์•ผํ•œ๋‹ค.

package movie;
 
import org.junit.Test;
 
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
    @Test
    public void should_return_0_when_just_created(){
        Movie movie = new Movie();
        assertThat(movie.averageRationg(),is(0));
    }
 
    @Test
    public void should_return_1_when_was_rated(){
        Movie movie = new Movie();
        movie.rate(1);
        assertThat(movie.averageRationg(),is(1));
    }
}

10) Compile ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์†Œ๋Ÿ‰์˜ ์ฝ”๋“œ ์ถ”๊ฐ€

package movie;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class Movie {
    public Integer averageRationg() {
        return 0;
    }
 
    public void rate(int rate) {
 
    }
}

Test ๊ฒฐ๊ณผํ™”๋ฉด

averageRationg Return ๊ฐ’์ด ์–ด๋– ํ•œ ์ƒํ™ฉ์—์„œ๋„ 0์ด๋‹ค. ๊ณ ์ณ์„œ Test๋ฅผ ํ†ต๊ณผ์‹œํ‚ค์ž.

package movie;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class Movie {
    private int sumOfRate = 0;
    private int countOfRate = 0;
 
    public Integer averageRationg() {
        return sumOfRate/ countOfRate;
    }
 
    public void rate(int rate) {
        this.sumOfRate += rate;
        this.countOfRate++;
    }
}

Test ๊ฒฐ๊ณผํ™”๋ฉด

countOfRate๊ฐ€ 0์ผ ๊ฒฝ์šฐ์— 0์œผ๋กœ ๋‚˜๋ˆ„์—ˆ์„ ๊ฒฝ์šฐ Exception ๋ฐœ์ƒ โ†’ 0์ผ๋•Œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ

package movie;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class Movie {
    private int sumOfRate = 0;
    private int countOfRate = 0;
 
    public Integer averageRationg() {
        return countOfRate == 0 ? 0 : sumOfRate/ countOfRate;
    }
 
    public void rate(int rate) {
        this.sumOfRate += rate;
        this.countOfRate++;
    }
}

Test ๊ฒฐ๊ณผํ™”๋ฉด

11) Refactoring - TestCase์˜ ์ค‘๋ณต๋˜๋Š” ๋ถ€๋ถ„ ์ œ๊ฑฐํ•˜๊ธฐ

package movie;
 
import org.junit.Test;
 
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
    @Test
    public void should_return_0_when_just_created(){
        Movie movie = new Movie(); // ์ค‘๋ณต
        assertThat(movie.averageRationg(),is(0));
    }
 
    @Test
    public void should_return_1_when_was_rated(){
        Movie movie = new Movie(); // ์ค‘๋ณต
        movie.rate(1);
        assertThat(movie.averageRationg(),is(1));
    }
}

Tips : Intellj Refactor - Field - Setup Method ์ž๋™์œผ๋กœ ํ•ด์ค€๋‹ค.

package movie;
 
import org.junit.Before;
import org.junit.Test;
 
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
 
/**
 * Created by NESOY on 2017-01-31.
 */
public class MovieTest {
 
    private Movie movie;
 
    @Before
    public void setUp() throws Exception {
        movie = new Movie();
    }
 
    @Test
    public void should_return_0_when_just_created(){
        Movie movie = this.movie; // ๋ถˆํ•„์š”ํ•˜๋ฏ€๋กœ ์ œ๊ฑฐํ•œ๋‹ค.
        assertThat(movie.averageRationg(),is(0));
    }
 
    @Test
    public void should_return_1_when_was_rated(){
        Movie movie = this.movie; // ๋ถˆํ•„์š”ํ•˜๋ฏ€๋กœ ์ œ๊ฑฐํ•œ๋‹ค.
        movie.rate(1);
        assertThat(movie.averageRationg(),is(1));
    }
}

12) Averaging Rating Test Case๋งŒ๋“ค๊ธฐ โ†’ 3,5์„ ์ฃผ์—ˆ์„๋•Œ Average Rate๊ฐ€ 4์ด ๋‚˜์™€์•ผํ•œ๋‹ค.

@Test
   public void should_return_4_when_3_and_5_were_rated(){
       movie.rate(3);
       movie.rate(5);
       assertThat(movie.averageRationg(),is(4));
   }

Test ๊ฒฐ๊ณผํ™”๋ฉด

3. ์‹ค์ œ๋กœ ํ•ด๋ณธ TDD ์žฅ์ 

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

๊ฒฐ๋ก  : ๋งค์šฐ ์ข‹๋‹ค. ์•ž์œผ๋กœ ์Šต๊ด€๋“ค์—ฌ์•ผ๊ฒ ๋‹ค.

Intellj ๋‹จ์ถ•ํ‚ค

  • ํ™”๋ฉด ๋‚˜๋ˆ„๊ธฐ : Ctrl + Shift + A + Split Vertical
  • Refactoring Field : Ctrl + Shift + A + Field ( Ctrl + Alt + F)

Books

  • ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ - ์ผ„ํŠธ ๋ฒก

Reference