-
Spock Test Framework ์ ๋ฆฌHello, World ๐ป/Test โ 2020. 12. 7. 23:54
JUnit 5 ์ ์ข ๋ ์ธ์ธํ๊ฒ ๊ณต๋ถํด๋ณผ๊น ํ๋ค ์๋ก์ด ์ธ์ด์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํด๋ณด๊ณ ์ถ์ด์ Spock Framework ๋ฅผ ๊ณต๋ถํ๊ฒ ๋์๋ค.
Spock ๋ Java, Groovy ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ํ ์คํธ ํ๋ ์์ํฌ๋ค. BDD(Behaviour-Driven Development) ํ๋ ์์ํฌ๋ผ๊ณ ๋ ํ๋ค. Groovy ์ธ์ด๋ก ์์ฑํ ์ ์๊ณ , ์๋ฆ๋ต๊ณ ํํ๋ ฅ์ด ๋ฐ์ด๋๋ค๊ณ ์๊ฐํ๊ณ ์๋ค(ใ ใ ). JUnit Runner ๋๋ถ์ ๋๋ถ๋ถ์ IDE, ๋น๋ ํด, CI ์๋ฒ์์ ๊ตฌ๋์ํฌ ์ ์๋ค๊ณ ํ๋ค.
์งง๊ฒ ์ผ์ฃผ์ผ์ ๋ Spock ๋ฅผ ์ข ์จ๋ณด๊ณ ๋๋ ์ ์ ํ์คํ JUnit ๋ณด๋ค ๋ ์ฝ๊ณ ๋ช ์์ ์ธ ๋๋์ด ์์๋ค. ์๋ฅผ ๋ค์ด JUnit ์ ์ธ ๋ ํ ์คํธ ๋ฉ์๋์ ์ฃผ์์ผ๋ก given, when, then ์ด๋ผ๊ณ ์์ฑํ๋๋ฐ, Spock ์์๋ ๊ฐ๊ฐ์ ๋ธ๋ก์ด๋ผ๊ณ ํ๊ณ given: when: ์ด๋ ๊ฒ ๋ธ๋ก์ ๋ฃ์ด์ค์ผ ๋์ํ๋ค. ์ข ๋ ๋ช ์์ ์ผ๋ก ์์๋ณผ ์ ์์ด์ ์ข์๋ค. ๋ JUnit ์ ๊ฐ์ ๋ฉ์๋ ์ด๋ฆ์ด ์ฌ๋ฌ ํจํค์ง์ ์กด์ฌํด์ ์ํ๋ ๊ฒ์ ์ฌ์ฉํ๋ ค๋ฉด ์ธ์์ ์ฌ์ฉํ์ด์ผ ํ๋๋ฐ, Spock ๋ All In One ์ค๋ฌ์ด ๋๋์ด ์์๋ค. ๊ทธ๋ฆฌ๊ณ Spock ์ ์๋ฌ ๋ฉ์ธ์ง๋ ๋ณด๊ณ ๊น์ง ๋๋๋ค! ๋ณด๋ฉด ์๋ฆฌ๋ผ. ๋ ์ฐ๋ค ๋ณด๋ฉด ์ฅ๋จ์ ์ ๋๋ผ๊ฒ ์ง๋ง ์ฒซ์ธ์์ ๊ตฟ๊ตฟ์ด๋ค.
Gradle ์์กด์ฑ ์ค์
plugins { id 'java' id 'groovy' } apply plugin: 'groovy' group 'me.ohhhmycode' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'org.spockframework', name: 'spock-core', version: '1.3-groovy-2.5' }
- spock-core ์์กด์ฑ์ ์ถ๊ฐํด์ค๋ค. (mvnrepository.com)
- ๊ฐ์ธ์ ์ผ๋ก Intellij ์ปค๋ฎค๋ํฐ ๋ฒ์ ์ ์ฌ์ฉ ์ค์ธ๋ฐ, plugins ์ค์ ์ ํด์ค์ผ Test ๋ฅผ ์์ฑํ์ ๋ 'No tasks available' ๊ฐ ๋จ์ง ์๊ณ ํ ์คํธ๋ฅผ ์คํํ ์ ์์๋ค.์์กด์ฑ ์ค์ ๊ณผ ํ๋ฌ๊ทธ์ธ ์ค์ ์ด ๋๋๋ฉด /src/test/groovy ๋๋ ํ ๋ฆฌ๋ฅผ ์์ฑํ๊ณ groovy ๋๋ ํ ๋ฆฌ์ ํ ์คํธ ํด๋์ค๋ฅผ ์์ฑํ๋๋ก ํด์ผ ํ๋ค.
import spock.lang.* class MySpockTest extends Specification { def "์ฒซ๋ฒ์งธ ํ ์คํธ ๋ฉ์๋"() { given: def x = 5; def y = 10; when: def sum = x + y; then: sum == 15; } }
์ฒซ ๋ฒ์งธ ํ ์คํธ ํด๋์ค๋ฅผ ์์ฑํด๋ดค๋ค. Specification ๋ผ๋ ํด๋์ค๋ฅผ ์์๋ฐ๊ณ ์๋ ๊ฒ์ด ๋ณด์ด๋๋ฐ spock ํ ์คํธ ํด๋์ค๋ spock.lang ํจํค์ง์ Specification ์ ์์๋ฐ์์ผ ํ๋ค. Specification ํด๋์ค์๋ ํ ์คํธ๋ฅผ ์์ฑํ๋๋ฐ ํ์ํ ์ค์ํ ๊ฒ๋ค์ด ๋๋ถ๋ถ ์๋ค.
์๋๋ถํฐ๋ ๋ฌธ์ ์์ฝ๊ณผ ์ฝ๋ ์์ ๋ก ํ์ด๋ณด๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
Specifications
Spock ์์๋ ํ ์คํธ๋ฅผ ์์ฑํ๋ ๊ฒ์ Specification ์ ์์ฑํ๋ค๊ณ ํ๋๋ฐ, Specifications ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ด๋ค ๊ธฐ๋ฅ(feature)์ ๋ํด ์์๋๋ ๋์๋ค์ ์์ฑํ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค. ๊ทธ๋์ Spock๋ ์์ ํ ์คํธ ๋ฉ์๋๋ฅผ Feature Method ๋ผ๊ณ ํ๋ค.
์์์ ๋งํ ๊ฒ์ฒ๋ผ Specification ํด๋์ค์ ํ ์คํธ๋ฅผ ์์ฑํ๋๋ฐ ํ์ํ ๋๋ถ๋ถ์ ๊ฒ์ด ์๋ค. ๊ทธ๋ฆฌ๊ณ Specification ์๋ Sputnik ๋ผ๋ JUnit Runner ๊ฐ ์๋๋ฐ, Sputnik ๋ก Java ๊ฐ๋ฐ ํด๋ค์ด๋ ๋น๋ ํด์์ Spock ๋ฅผ ์คํํ ์ ์๋ ๊ฒ์ด๋ผ๊ณ ํ๋ค.Fields
import spock.lang.* class MySpockTest extends Specification { static def classStore = new HashSet(); def myClass = new MyClass(); def "ํ๋ ํ ์คํธ 1"() { when: classStore.add(myClass); then: classStore.size() == 1 println myClass // spocktest.MyClass@4a35cdc8 } def "ํ๋ ํ ์คํธ 2"() { when: classStore.add(myClass) then: classStore.size() == 2 println myClass // spocktest.MyClass@6915ed51 } }
์ ํ ์คํธ๋ ํต๊ณผํ๋ค. (Feature Method ๊ฐ ์์ฐจ์ ์ผ๋ก ์์์ ์๋๋ก ์คํ๋๋ ๊ฒ ๊ฐ๋ค(?) ํ์ธํด๋ด์ผ์ง..)
classStore, myClass ๊ฐ ์ ์ธ๋์ด ์๋ ์์ญ์ Instance Fields ๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ ํ ์คํธ์์ ์ฌ์ฉํ๋ ๊ฐ์ฒด๋ค์ ์ ์ธํ๊ณ ์ด๊ธฐํํ๋ฉด ๋๋ค. (setup() ๋ฉ์๋์์ ํ๋ ๊ฒ๊ณผ ๋์ผ) ์์๋ฌ์ผ ํ ๊ฒ์ ์ธ์คํด์ค ํ๋์์ ์ ์ธํ๊ณ ์ด๊ธฐํํ ๊ฐ์ฒด๋ค์ ๊ฐ๊ฐ์ Feature Method ์์ ๊ณต์ ๋์ง ์๋๋ค. ๋ฉ์๋๋ ํญ์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฐ์ ํ ์คํธ๋ ๋งค๋ฒ ๋ถ๋ฆฌ๋ ํ๊ฒฝ์์ ํ ์คํธ๋ฅผ ์ํํ ์ ์๋ค.
๊ฐ์ฒด๋ฅผ ๋ชจ๋ Feature Method ์์ ๊ณต์ ํด ์ฌ์ฉํ๊ณ ์ถ์ ๊ฒฝ์ฐ @Shared ์ด๋ ธํ ์ด์ ์ ๋ถ์ด๋ฉด ๋๋ค. (@setupSpec() ๋ฉ์๋์์ ํ๋ ๊ฒ๊ณผ ๋์ผ) ์๋ ๋ฉ์๋๋ฅผ ๋ณด์.import spock.lang.* class MySpockTest extends Specification { static def classStore = new HashSet(); @Shared def myClass = new MyClass(); def "ํ๋ ํ ์คํธ 1"() { when: classStore.add(myClass); then: classStore.size() == 1 println myClass // spocktest.MyClass@217f3ce1 } def "ํ๋ ํ ์คํธ 2"() { when: classStore.add(myClass) then: classStore.size() == 1 println myClass // spocktest.MyClass@217f3ce1 } }
classStore ์ฒ๋ผ static ๋ณ์๋ฅผ ์ฌ์ฉํด ๊ฐ Feature Method ์์ ๊ณต์ ํ ์ ์๋ค. ๋ค๋ง static ๋ณ์๋ ์ ๋ง ์์๋ฅผ ์ฌ์ฉํ ๋๋ง ์ฌ์ฉํ๊ณ ๊ทธ๊ฒ ์๋๋ฉด @Shared ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๊ณ ์๋ค. ์์์ classStore ๋ ์ฌ์ค @Shared ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ ์ฌ์ฉํ๋ ๊ฒ ๋ ๋ง์ ๊ฒ ๊ฐ๋ค.
Fixture Methods
def setupSpec() {} // ์ฒซ๋ฒ์งธ feature method ์คํ ์ ์ ํ๋ฒ ์คํ def setup() {} // ๋ชจ๋ feature method ์คํ ์ ์ ๋งค๋ฒ ์คํ def cleanup() {} // ๋ชจ๋ feature method ์คํ ํ์ ๋งค๋ฒ ์คํ def cleanupSpec() {} // ๋ง์ง๋ง feature method ์คํ ํ์ ํ๋ฒ ์คํ
Fixture Methods ๋ ํ ์คํธ ํ๊ฒฝ์ ์ธํ ํ๊ฑฐ๋ ์ ๋ฆฌํ๋ ์ญํ ์ ํ๋ค. ํ์๋ ์๋๋ฏ๋ก ์ ํ์ ์ผ๋ก ํ๋ฉด ๋๋ค.
import spock.lang.* class MySpockTest extends Specification { @Shared def sharedObj; def obj; def setupSpec() { sharedObj = new MyClass() } def setup() { obj = new MyClass() } // feature methods.. }
setupSpec() ์์ ์ด๊ธฐํํ๋ ๋ณ์๋ค์ @Shared ์ด๋ ธํ ์ด์ ์ ๋ถ์ด๋ ๊ฐ, static ๋ณ์์ด์ด์ผ ํ๋ค.
์๋๋ Fixture Methods ๋ค์ด ์คํ๋๋ ์์์ด๋ค.- super.setupSpec
- sub.setupSpec
- super.setup
- sub.setup
- feature method
- sub.cleanup
- super.cleanup
- sub.cleanupSpec
- super.cleanupSpec
Feature Methods
import spock.lang.* class MySpockTest extends Specification { def "ํ๊ธ๋ก ํ ์คํธ ๋ฉ์๋ ์ด๋ฆ์ ์์ฑํ ์ ์๋ค"() { } }
๊ฐ์ฅ ์ค์ํ Feature Methods ์ด๋ค. ์์์ ๋งํ๋ฏ, Feature Method ๋ ๊ฐ๋ฐํ๋ ํ๋ก๊ทธ๋จ์ด Spec ๋๋ก ์ ๋์ํ๋์ง ํ ์คํธ ๋ฉ์๋๋ฅผ ์์ฑํด ํ์ธํ๋ ๊ฒ์ด๋ค. ๊ด๋ก์ ์ผ๋ก ๋ฌธ์์ด ๋ฆฌํฐ๋ด๋ก Feature Method ์ ์ด๋ฆ์ ์ง๋๋ค๊ณ ํ๋ค. JUnit ์ ์ฌ์ฉํ ๋๋ณด๋ค ์ข ๋ ๋ช ํํ๊ฒ ํ ์คํธ ๋ช ์ ์ง์ ์ ์์ ๊ฒ ๊ฐ๋ค.
Feature Method ๋ 4๋จ๊ณ๋ก ๊ตฌ์ฑ๋๋ค.- Set Fixture
- ํ๋ก๊ทธ๋จ ๊ธฐ๋ฅ ์ํ (Stimulus)
- ํ๋ก๊ทธ๋จ ๊ธฐ๋ฅ ์ํ ํ์ Response ํ์ธ (Describe the response)
- Clear Fixture
1, 4๋จ๊ณ๋ ์์์ ๋ณธ ๊ฒ์ฒ๋ผ ์ ํ์ ์ผ๋ก ํ๋ฉด ๋๋ค. ๋ฌธ์์ 2, 3 ๋จ๊ณ๋ Stimulus to the system.., Describe the response.. ๋ผ๊ณ ๋์ด ์๋๋ฐ ๋๋ฆ๋๋ก ํ์ด์จ๋ดค๋ค..^^; stimulus, response ๋ ํญ์ ์กด์ฌํด์ผํ๊ณ , ํ๋ฒ ์ด์ ๋์ฌ ์ ์๋ค.
Block
Spock ์ ์์์ ๋งํ 4๋จ๊ณ๋ฅผ ์คํํ๊ธฐ ์ํด ๋ด์ฅ๋ ๊ธฐ๋ฅ์ด ์๋ค. ์ด๊ฑธ Block ๋ผ๊ณ ํ๋ค.
given, when, then, expect, cleanup, where ๋ผ๊ณ ํ๋ 6๊ฐ์ ๋ธ๋ก์ด ์๋ค. ๊ฐ ๋ธ๋ก์ given:, when: ๊ณผ ๊ฐ์ ๋ผ๋ฒจ๋ก ์์๋๊ณ ๋ค์ ๋ธ๋ก์ด ๋์ฌ ๋๊น์ง์ ์์ญ์ด ๋ค ํด๋น ๋ธ๋ก์ ์์ญ์ด ๋๋ค.
๋ฉ์๋์ ์์๋ถํฐ ์ฒซ ๋ฒ์งธ ๋ธ๋ก์ด ๋์ฌ ๋๊น์ง์ ์์ญ์ given ๋ธ๋ก์ด ๋๋ค. given ์ด ๊ฐ์ฅ ๋จผ์ ์์ผํ๊ณ given ์ ์๋ตํ ์ ์๋ค. ํ์ง๋ง ๋ช ์์ ์ผ๋ก given: ์ ์จ์ฃผ๋๊ฒ ์ข์ง ์์๊น ์ถ๋ค.Feature Method ๋ ์ ์ด๋ ํ๋์ ๋ธ๋ก์ ๊ฐ์ง๊ณ ์์ด์ผ ํ๊ณ , ๋ธ๋ก์ ์ค์ฒฉํ ์๋ ์๋ค. ์๋ ๊ทธ๋ฆผ์ ๋ณด๊ณ ๊ฐ ๋จ๊ณ์ ํด๋นํ๋ Block์ ํ์ธํ ์ ์๋ค. where ๋ ์ข ํน์ํ ์ญํ ์ ๊ฐ์ง๊ณ ์๋ค๊ณ ํ๋ค.
Given Block
import spock.lang.* class MySpockTest extends Specification { def "๋ํ๊ธฐ ํ ์คํธ"() { given: def x = 10; def y = 20; } }
Given ๋ธ๋ก์์๋ ํ ์คํธ์ ํ์ํ ๊ฐ์ฒด๋ ํ๊ฒฝ์ ์ค๋นํ๋ ๊ณณ์ด๋ค. Given ๋ธ๋ก ์์ ๋ค๋ฅธ ๋ธ๋ก์ ์ฌ ์ ์๊ณ , Given: ๋ผ๋ฒจ์ ์๋ตํ ์ ์๋ค. given: ๋ผ๋ฒจ ๋์ ์ setup: ๋ผ๋ฒจ์ ์ฌ์ฉํด๋ ๋๋ค.
When, Then Block
import spock.lang.* class MySpockTest extends Specification { def "๋ํ๊ธฐ ํ ์คํธ"() { given: def x = 10; def y = 20; when: // stimulus def sum = x + y then: // response sum == 30 } }
when, then ๋ธ๋ก์ ํญ์ ๊ฐ์ด ์ฌ์ฉํด์ผ ํ๊ณ , ์ฌ๋ฌ๋ฒ ์ฌ์ฉํ ์ ์๋ค. when ์์ ํ ์คํธ ์คํํ๊ณ -> then ์์ ํ์ธํ๋ค ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค. when ๋ธ๋ก์์๋ ํ ์คํธํ๋ ค๋ ์ฝ๋๋ฅผ ์์ฑํ๊ฑฐ๋, ์์๋ก ๋ญ ํ์ํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ ๋ฐ๋ฉด์, then์ ์กฐ๊ฑด ํ์ธ(Conditions), ์์ธ ํ์ธ(Exception Conditions), ์ธํฐ๋์ (Interaction), ๋ณ์ ์ ์์ ํด๋นํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋๋ก ์ ํ๋์ด ์๋ค.
์กฐ๊ฑด ํ์ธ, Conditions
Conditions ์์๋ boolean ํํ์์ ์ฌ์ฉํด์ ์กฐ๊ฑดํ์ธ(์ํํ์ธ)์ ํ๋ค. JUnit ์ assertions ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค. then ์ ํ์คํ์ค์ด ์กฐ๊ฑด์ ํ์ธํ๋ assert ๋ฌธ์ด ๋๋ค.
package spocktest import spock.lang.* class MySpockTest extends Specification { def "Stack ํ ์คํธ"() { given: def stack = new Stack(); def number = 10; when: stack.push(number) then: // boolean ํํ์์ผ๋ก ์์ฑ !stack.empty() stack.size() == 1 stack.peek() == number } }
Condition ์ ํญ์ 1~5๊ฐ ์ ๋๋ก ์ ์งํ๋ ๊ฒ์ด ์ข๋ค๊ณ ํ๋ค. ๊ทธ ์ด์์ด ๋๋ฉด Condition ๋ค์ด ์ ๋ง ์ฐ๊ด๋์ด ์๋์ง ํ์ธํด๋ณด๊ณ , ํ ์คํธ ๋ฉ์๋๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ๋ ๋๋ ์ ์๋์ง ๋ด์ผ ํ๋ค.
์กฐ๊ฑด์ด ๋ง์ง ์๋ ๊ฒฝ์ฐ๋ ์๋์ฒ๋ผ ํผ๋๋ฐฑ์ ์ค๋ค. ๊ฐ์ธ์ ์ผ๋ก ํผ๋๋ฐฑ ๋ฉ์ธ์ง๋ฅผ ๋ณด๊ณ ๋ง์ด ๋๋๋ค;
package spocktest import spock.lang.* class MySpockTest extends Specification { def "Stack ํ ์คํธ"() { given: def stack = new Stack(); def number = 10; when: stack.push(number) then: !stack.empty() stack.size() == 2 // ํ๋ฆฐ ์กฐ๊ฑด stack.peek() == number; } }
Condition not satisfied: stack.size() == 2 | | | [10] 1 false at spocktest.MySpockTest.Stack ํ ์คํธ(MySpockTest.groovy:29)
stack ์ ํ์ฌ ์ฌ์ด์ฆ๋ ํ์ธํด์ ๋ณด์ฌ์ฃผ๋ค๋... ๐
์์ธ ํ์ธ, Exception Conditions
package spocktest import spock.lang.* class MySpockTest extends Specification { def "Stack Exception ํ ์คํธ"() { given: def stack = new Stack(); when: stack.pop() then: thrown(EmptyStackException) stack.empty() } }
when ์์ ์์ธ๋ฅผ ๋ฐ์์์ผ์ผ ํ๋ ๊ฒฝ์ฐ then ์์ thrown() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ํ์ธํ ์ ์๋ค. ๊ฑฐ์ ๊ทธ๋ฅ ์ฝ๋๋ง ์ ๊น ์ฝ์ผ๋ฉด ์ดํดํ ์ ์๋๋ก ์ฝ๊ฒ ๋์ด ์๋ ๊ฒ ๊ฐ๋ค. then ๋ธ๋ก์์ ์ด๋ ๊ฒ ์์ธ๋ฅผ ํ์ธํ๋ ์กฐ๊ฑด์ ํ ๋ฒ๋ง ์ฌ์ฉํ ์ ์๋ค.
์์ธ ๊ฐ์ฒด๋ฅผ ๋ฐ์ผ๋ ค๋ฉด ์๋์ฒ๋ผ ํ๋ฉด ๋๋ค. ๋ ์ค์ ์ ํํด ์ฌ์ฉํ๋ฉด ๋ ๊ฒ ๊ฐ์ง๋ง, ๋ ๋ฒ์งธ ๋ฐฉ๋ฒ์ด ๋ ๋์ ๊ฒ ๊ฐ๋ค.
when: stack.pop() then: def e = thrown(EmptyStackException) e.message == "error message"
when: stack.pop() then: EmptyStackException e = thrown() e.message == "error message"
๋๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์ผ๋ฉด ๊ฐํ์ ์ผ๋ก ์ฒดํฌ๊ฐ ๊ฐ๋ฅํ๊ณ , IDE ์์ ์ฝ๋ ์๋ ์์ฑ ์ง์๋ ๋ ์ ๋ฐ์ ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ข ๋ ๊ฐ๋ ์ฑ์ด ์ข์ ์ฝ๋์ธ ๊ฒ ๊ฐ๋ค.
๋ง์ฝ ๋ฐ์ํ์ง ์์์ผ ํ ์์ธ ์ฒดํฌ๋ ํด์ผ ํ ๊ฒฝ์ฐ notThrown() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
then: notThrown(NullPointerException)
Interations
์ธํฐ๋์ ์ Mock ๊ฐ์ฒด๋ฅผ ์์ฑํด ํน์ ๋ฉ์๋๊ฐ ๋ช ๋ฒ ํธ์ถ๋๋์ง, ๋ฉ์๋์ ๋ฐํ ๊ฐ์ Stubbing ํ ์๋ ์๋ค. ์ธํฐ๋ ์ ์ ๋ํด์๋ ๋ด์ฉ์ด ์ ๋ง ๋ง์ ๊ฒ ๊ฐ๋ค. ์ง๊ธ์ ์๋ ์์ ๋ง ๋ณด๊ณ ๋์ด๊ฐ๊ณ Interaction์ ๋ ์ก๊ณ ๋ ๋ด์ผํ ๊ฒ ๊ฐ๋ค.
then: 1 * subscriber.receive("hello") // 1๋ฒ ํธ์ถ subscriber.receive(_) >> "ok" // receive(_) ์ด ํธ์ถ๋ ๊ฒฝ์ฐ "ok" ๋ฐํ subscriber.receive(_) >> { throw new InternalError("ouch") } // receive(_) ์ด ํธ์ถ๋ ๊ฒฝ์ฐ ์์ธ ๋ฐ์
Expect Blocks
then ๋ธ๋ก๊ณผ ๋น๊ตํด์ Expect ๋ธ๋ก์ ์กฐ๊ฑด ํ์ธ, ๋ณ์ ์ ์๋ง ํ ์ ์๋ค๋ ์ ์์ ์ข ๋ ์ ํ์ ์ด๋ค. Expect ๋ธ๋ก์ ์ฝ๋ ๋์ => ์กฐ๊ฑด ํ์ธ์ด ํ๋์ ํํ์์์ ํ์ธํ๋ ๊ฒ์ด ๋ ์์ฐ์ค๋ฌ์ธ ๋ ์ฌ์ฉํ๋ฉด ์ข๋ค. Math.max() ๋ฉ์๋๊ฐ ์๊ฐ ๋ ์ ์๊ฒ ๋ค.
when: def max = Math.max(1, 3) then: max == 3
expect: Math.max(1, 3) == 3
Cleanup Blocks
given: def file = new File("/some/path") file.createNewFile() // ... cleanup: file.delete()
cleanup ๋ธ๋ก ๋ค์๋ where ๋ธ๋ก๋ง ์ฌ ์ ์๊ณ , cleanup ๋ธ๋ก์ ๋ฐ๋ณต๋ ์ ์๋ค. ์์ ์ฝ๋์ฒ๋ผ ํ ์คํธ์์ ์ฌ์ฉํ ๊ฐ์ฒด๋ฅผ ํ ๋น ํด์ ํ๊ฑฐ๋ ์ญ์ ํ๋ ๋ฑ์ ํ๋์ ํ๋ค. ๊ฐ์ฒด๋ ์ฌ์ค Garbage Collector๊ฐ ์ ๋ฆฌํด์ฃผ๋๊น cleanup ์์ ํน๋ณํ ์ ๊ฒฝ์ ์ฐ์ง ์(?)์๋ ๊ด์ฐฎ์ง๋ง, ํ์ผ ์์คํ ์ด๋ DB ์ปค๋ฅ์ , ๋คํธ์ํฌ ์๋น์ค ๊ฐ์ ๊ฒ๋ค์ ์ ์ ๋ฆฌํ ํ์๊ฐ ์๋ค. ๋ชจ๋ feature ๋ฉ์๋์์ ์ฌ์ฉํ๋ ์์์ผ ๊ฒฝ์ฐ๋ cleanup() Fixture ๋ฉ์๋๋ฅผ ์ฐ๋ฉด ๋๋ค.
Where Blocks
where ๋ธ๋ก์ ํญ์ ๊ฐ์ฅ ๋ง์ง๋ง์ ์ค๊ณ , ๋ฐ๋ณต๋ ์ ์๋ค. data-driven ํ ์คํธ๋ฅผ ํ ๋ ์ฌ์ฉ๋๋ค.
import spock.lang.* class MySpockTest extends Specification { def "where ๋ธ๋ก ์ฌ์ฉํ๊ธฐ"() { expect: Math.max(a, b) == c where: a << [5, 3] b << [1, 9] c << [5, 9] } }
Math.max(5, 1) == 5
Math.max(3, 9) == 9 ์์๋๋ก ์ด๋ ๊ฒ ํ๊ฐ๊ฐ ๋ ๊ฒ์ด๋ค. ์๋์ฒ๋ผ data tables ์ ๋ง๋ค์ด ์์ฑํ ์๋ ์๋ค.import spock.lang.* class MySpockTest extends Specification { def "where ๋ธ๋ก ์ฌ์ฉํ๊ธฐ"() { expect: Math.max(a, b) == c where: a | b | c 5 | 1 | 5 3 | 9 | 9 } }
๊ฐ๋ง ๋ฐ๊ฟ์ ํ ์คํธํ๋ ์ฝ๋๋ฅผ ์์ฑํ ๋ ์ ๋ง ํธํ ๊ฒ ๊ฐ๋ค. Where ๋ธ๋ก์์ ํ ์ ์๋ Data Driven ํ ์คํ ๋ ๋ด์ฉ์ด ์ ๋ง ๋ง์๋ค. ์ด๊ฒ๋ ์ธํฐ๋ ์ ๊ณผ ๋๋ถ์ด์ ๋ ๊ณต๋ถํ๋ฉด์ ์ฌ์ฉํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.
๋ง๋ฌด๋ฆฌ
์ด๋ ๊ฒ ์ด๋ค ํ๋ ์์ํฌ์ ๋ํด์ ๊ณต๋ถํ ๋ ํญ์ ๋๊ตฐ๊ฐ์ ๋ธ๋ก๊ทธ๋ ์์์ ์ฐธ๊ณ ํ๋ฉฐ ๊ณต๋ถํ์ง ์ด๋ ๊ฒ ์จ๋ณธ ์ ์ด ์ฒ์์ด๋ค. ์๊ฐ๋ณด๋ค ์ ๋ง ๋ง์ ์๊ฐ์ด ๋ค์ด๊ฐ๊ณ ์์ด๋ฅผ ์ฝ๊ณ ์ ๋นํ ํ๊ธ ํํ์ผ๋ก ๋ฐ๊พธ๋ ๊ฒ์ด ์ผ๋ง๋ ์ด๋ ค์ด์ง ์์ผ ๋ค์ ๊นจ๋ซ๋๋ค. ๊ณต์ ๋ฌธ์๋ฅผ ์ฐจ๋ถํ ์ฝ๋ค ๋ณด๋ ๋ค๋ฅธ ์ฌ๋๋ค์ ๋ธ๋ก๊ทธ๋ฅผ ๋ณผ ๋์ ๋ค๋ฅด๊ฒ ์ข ๋ ๊ผผ๊ผผํ๊ฒ ์๊ฒ ๋๊ณ , ๋ธ๋ก๊ทธ์ ์ฐ๋ค ๋ณด๋ ๋ ์ ์ ๋ฆฌ๊ฐ ๋๋ ๊ฒ์ด ์๋ ๊ฒ ๊ฐ๋ค.
Spock ํ๋ ์์ํฌ๋ JUnit ๊ณผ ๋น๊ตํด์๋ ์ข ๋ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๊ณ , ๋ ๋ช ์์ ์ธ ํ ์คํธ๋ฅผ ์์ฑํ ์ ์๋ ๊ฒ ๊ฐ๋ค. ๋ธ๋ก๋ ๊ทธ๋ ๊ณ ์๋ฌ ๋ฉ์ธ์ง๋ ์ ๋ง ์น์ ํ๊ฒ ๋ณด์ฌ์ค๋ค. ์ข ๋ ์ฐ๋ฉด์ ๊ณต๋ถํ๋ค๋ณด๋ฉด ์ฅ๋จ์ ๋ ๋ ์๊ฒ๋๊ณ JUnit ๊ณผ ๋ ๋ง์ ๋น๊ต๋ฅผ ํ ์ ์์ ๊ฒ ๊ฐ๋ค.
Spock ๋ก Spring ๋ ์ผ๋ฅธ ํ ์คํธ๋ฅผ ํด๋ณด๊ณ ์ถ๋ค!
References
spockframework.org/spock/docs/1.3/spock_primer.html
spockframework.org/spock/docs/1.3/interaction_based_testing.html
d2.naver.com/helloworld/568425