====== Spock ====== * [[https://github.com/spockframework/spock|Spock]] * Test 코드가 설계 명세서 역할을 할 수 있을 정도로 가독성 높은 테스트를 짤 수 있다. * [[java:spock:spock_reports|Spock Reports]] 테스트 결과를 문서화 해준다 * [[http://docs.spockframework.org|Spock Framework Reference Documentation]] [[http://spockframework.github.io/spock/docs/1.0/index.html|1.0]] * [[http://meetspock.appspot.com/|Spock Web Console]] * [[https://leanpub.com/spockframeworknotebook|Spocklight notebook]] * [[https://solidsoft.wordpress.com/2018/09/03/spock-1-2-hassle-free-spring-beans-mocking-in-integration-tests/|Spock 1.2 – hassle-free Spring beans mocking in integration tests | Solid Soft]] ===== Dependencies ===== testCompile group: 'org.spockframework', name: 'spock-core', version: '1.1-groovy-2.4' testCompile group: 'cglib', name: 'cglib-nodep', version: '3.2.4' // Class Mocking 할 때 필요. ===== Spock Feature Method 기본형태(test method) ===== // 일반적인 테스트 def "Feature Method name"() { :given "생략가능" // 테스트용 데이터 초기화 :when // 테스트 대상 코드를 실행하고 실행 결과를 변수 등에 저장 :then // 테스트 결과에 대한 검증. // 각 줄은 boolean 결과를 내는 Statement 로 작성한다. // 만약 한 줄의 코드가 boolean statement가 아니고 복잡한 구문일 경우에는 // 그 안의 assert boolean 구문에 'assert somebooleanexpression' 형태로 assert를 붙여야한다. } // Data Driven 테스트 def "Feature method name #a - #b = #c"(변수 a, 변수 b, 변수 c) { expect: // 각 데이터 변수로 테스트 수행 where: "각 데이터 변수 설정" a | b || c 1 | 2 || 3 4 | 5 || 6 } ===== Mock Argument Capture ===== // Argument capture def extern = null 1 * mock.foo( { extern = it; it.size() > 0 }) // 1.2 방식 1 * mock.foo( { it.size() > 0 }) >> { extern = it[0] } // 1.3 방식 * void 메소드의 경우 아래 참조. ===== void method Mock - 인자 값 조정 혹은 throw exception ===== * [[http://farenda.com/spock/spock-framework-mock-throw-exception/|Spock Framework Mock throw exception - Java Programming Tutorials]] // an interface with two methods: exists(user), add(user) def userService = Mock(UserService) // a controller to test, that will use mock of the service: def controller = new UserController(userService) def 'should throw exception for invalid username'() { given: def username = 'Joffrey Baratheon' userService.exists(_ as User) >> false userService.add(_ as User) >> { User user -> throw new IllegalArgumentException(user.name) } when: controller.addUser(username) then: def e = thrown(IllegalArgumentException) e.message == username } ===== Mock 선언이 작동하지 않을 때 ===== * 대상 Class 혹은 Method가 ''**final**''로 선언돼 있으면 아무 오류없이 Mock 이 작동하지 않는다. ===== 기타 Mock ===== 0 * _ // 이 위 이후로는 어떠한 모의객체 호출 행위도 없어야 한다. ===== Spy ===== * ''Spy()'' 사용시 스파이 대상 객체에 필드 인젝션이 안된다. * Spy객체 생성 후 Spring의 [[https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/util/ReflectionTestUtils.html|ReflectionTestUtils]]를 이용해서 따로 주입해주면 된다. ===== 오류 / Error ===== * [[https://github.com/spockframework/spock/issues/491|Spock and Slf4j combination causes compilation error]] : Spock에서 ''@groovy.util.logging.Slf4j'' 사용시에 ''Error:Groovyc: The current scope already contains a variable of the name $spock_valueRecorder'' 오류가 발생하는 경우가 있다. Logger 직접 선언할 것. * 1.0 버전에서 메소드가 여러개 override 돼 있을 경우 Type 지정이 명확하지 않으면 잘못된 메소드를 호출할 수도 있다. // method 가 여러개로 override 돼 있을 경우 어떤 것이 호출될지 알 수 없음. 특히 return 도 서로 다를 때. expect: SomeClass.method(null) == null // 아래와 같이 파라미터와 리턴 타입을 모두 명시할 것. when: Result result = SomeClass.method(null as Request) then: result == null ===== Spock 1.3 & groovy 2.5 문제/변경점 ===== * [[http://spockframework.org/spock/docs/1.3/release_notes.html|1.3 Release Notes]] ==== where 의 변수명이 서로 다른 메소드에 동일하게 존재하는데 타입이 다를경우 ==== * groovy 2.4 로는 발생하지 않지만 2.5로 가면 ''where''의 변수명에 따라 문제가 발생함. 변수명 수정 필요. groovy 의 문제가 아니라 spock & groovy 2.5 조합일 때 발생하는 버그로 보임. * ''where'' 의 변수명이 전혀 다른 메소드에서 다른 타입으로 사용되면, 그 다른 타입으로 인식하는 문제. **type 이 다르면 변수 이름도 다르게 해야 한다.** [[https://github.com/spockframework/spock/issues/880|Parameter type is inferred wrong when local variable with same name is present in unrelated method · Issue #880 · spockframework/spock]] ==== Argument 의 각 줄을 assert 하게 변경되면서 capture 방식도 바뀜 ==== * 1.2 에서는 Argument 의 Closure 블록에서 조건을 체크하려면 전체를 하나의 true/false 를 반환하게 만들어야 했다. * 1.3 부터는 각 줄에 대해 ''then'' 처럼 각자 assert 를 수행하기 때문에 그럴 필요가 없어짐. // 1.2 방식 1 * mock.foo({ it.size() > 1 && it[0].length == 2 }) // 1.3 - 한줄 한줄의 boolean 반환 결과를 자동 체크함. 1 * mock.foo({ it.size() > 1 it[0].length == 2 }) * 그러나 그로인해서 Argument capture 방식 변경됨. [[https://github.com/spockframework/spock/issues/970|Argument capture and return value in 1.3-RC1 · Issue #970 · spockframework/spock]] // Argument capture def extern = null 1 * mock.foo( { extern = it; it.size() > 0 }) // 1.2 방식 1 * mock.foo( { it.size() > 0 }) >> { extern = it[0] } // 1.3 방식 ===== 참조 ===== * [[http://helloworld.naver.com/helloworld/568425|Spock으로 테스트하기]] * [[http://java.dzone.com/articles/spring-integration-testing|Spring Integration Testing with Spock | Javalobby]] * [[http://examples.javacodegeeks.com/core-java/spock-tutorial-beginners/|Spock Tutorial for beginners]] * [[http://www.petrikainulainen.net/programming/testing/writing-unit-tests-with-spock-framework-introduction-to-specifications-part-three/|Spock Framework Intoroduction]] * [[https://objectpartners.com/2014/04/08/spock-mock-cheatsheet/|Spock Mock Cheat Sheet]] * [[https://myshittycode.com/2014/08/08/spock-reading-test-data-from-csv-file/|Spock: Reading Test Data from CSV File | My Shitty Code]] * [[https://www.youtube.com/watch?v=GHrfO1OqhLs|Interesting nooks and crannies of Spock you (may) have never seen before - YouTube]] * [[http://mrhaki.blogspot.kr/2016/09/spocklight-custom-default-responses-for.html|Spocklight: Custom Default Responses for Stubs - Messages from mrhaki]]