33
Spockの基礎 @kiy0taka 1339日土曜日

Spockの基礎

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Spockの基礎

Spockの基礎@kiy0taka

13年3月9日土曜日

Page 2: Spockの基礎

自己紹介

•奥 清隆(おく きよたか)• @kiy0taka、id:kiy0taka

•株式会社ニューキャスト所属•名古屋在住JGGUG関西支部長•仕事:Groovy/Grailsとか

13年3月9日土曜日

Page 3: Spockの基礎

spock-shell

• シェルスクリプトのテストをSpockで書くSpock拡張

• http://git.io/spock-shell

13年3月9日土曜日

Page 4: Spockの基礎

import org.kiy0taka.spock.shell.ShellSpec

class GreetingSpec extends ShellSpec {

def "greet with custom message"() { given: export 'GREET', 'Goodbye'

when: run 'greeting.sh', 'Spock'

then: lines[0] == 'Goodbye Spock!' }}

13年3月9日土曜日

Page 5: Spockの基礎

dproofs

http://xmldo.jp/dproofs13年3月9日土曜日

Page 6: Spockの基礎

Agenda

• AST変換

• Specification

13年3月9日土曜日

Page 7: Spockの基礎

SpockTransform

• SpockのAST変換はこっから始まる

Spec spec = new SpecParser(errorReporter).build(clazz);spec.accept(new SpecRewriter(nodeCache, sourceLookup, errorReporter));spec.accept(new SpecAnnotator(nodeCache));

13年3月9日土曜日

Page 8: Spockの基礎

SpecParser

• テストクラスからSpecを組み立てる

13年3月9日土曜日

Page 9: Spockの基礎

Spec

public class Spec extends Node<Spec, ClassNode> { private final List<Field> fields private final List<Method> methods

private FixtureMethod initializerMethod private FixtureMethod sharedInitializerMethod

private FixtureMethod setupMethod private FixtureMethod cleanupMethod private FixtureMethod setupSpecMethod private FixtureMethod cleanupSpecMethod}

13年3月9日土曜日

Page 10: Spockの基礎

SpecRewriter

• テストクラスの原形がなくなるまで殴り続ける

• 仲間を呼んでボコボコにする

13年3月9日土曜日

Page 11: Spockの基礎

Rewriter連合

• SpecRewriter

• ConditionRewriter

• DeepBlockRewriter

• InteractionRewriter

• WhenBlockRewriter

13年3月9日土曜日

Page 12: Spockの基礎

AST変換前import spock.lang.*

class HelloSpock extends spock.lang.Specification {

def "length of Spock's and his friends' names"() { expect: name.size() == length

where: name | length "Spock" | 5 "Kirk" | 4 "Scotty" | 6 }}

13年3月9日土曜日

Page 13: Spockの基礎

AST変換後import spock.lang.*import org.spockframework.runtime.model.*

@SpecMetadata(line = 5, filename = 'HelloSpock.groovy')public class HelloSpock extends spock.lang.Specification {

@FeatureMetadata(parameterNames = ['name', 'length'], ...) public void $spock_feature_0_0(name, length) { Object $spock_valueRecorder = new ValueRecorder() SpockRuntime.verifyCondition(...) this.getSpecificationContext().getMockController().leaveScope() }

@DataProviderMetadata(line = -1, dataVariables = ['name']) public java.lang.Object $spock_feature_0_0prov0() { return ['Spock', 'Kirk', 'Scotty'] }

@DataProviderMetadata(line = -1, dataVariables = ['length']) public java.lang.Object $spock_feature_0_0prov1() { return [5, 4, 6] }

public Object $spock_feature_0_0proc(Object $spock_p0, Object $spock_p1) { Object name = $spock_p0 Object length = $spock_p1 return new Object[] }

}

13年3月9日土曜日

Page 14: Spockの基礎

フィールドdef date = new Date()

@FieldMetadata(name = 'date', line = 8, ordinal = 0)private Object date

private Object $spock_initializeFields() { date = new Date()}

13年3月9日土曜日

Page 15: Spockの基礎

@Sharedフィールド@Shareddef date = new Date()

@Shared@FieldMetadata(name = 'date', line = 8, ordinal = 0)protected volatile Object $spock_sharedField_date

private Object $spock_initializeSharedFields() { $spock_sharedField_date = new java.util.Date()}

13年3月9日土曜日

Page 16: Spockの基礎

フィーチャーdef 'test something'() { expect: 1 == 1}

@FeatureMetadata(parameterNames = [], name = 'test something', ...)public void $spock_feature_0_0() { Object $spock_valueRecorder = new ValueRecorder() SpockRuntime.verifyCondition(...) this.getSpecificationContext().getMockController().leaveScope()}

13年3月9日土曜日

Page 17: Spockの基礎

SpockRuntime.verifyCondition( $spock_valueRecorder.reset(), 'name.size() == length', 9, 9, null, $spock_valueRecorder.record( 5, $spock_valueRecorder.record( 3, $spock_valueRecorder.record(0, name) .$spock_valueRecorder.record(1, 'size')() ) == $spock_valueRecorder.record(4, length) ))

PowerAssert

※ GroovyのPowerAssertとは別実装13年3月9日土曜日

Page 18: Spockの基礎

thrown()when:def expected = new Calc().add(1, 1)then:thrown(Exception)

@FeatureMetadata(parameterNames = [], name = 'test something', ...)public void $spock_feature_0_0() { Object expected = this.getSpecificationContext().setThrownException(null) try { expected = new Calc().add(1, 1) } catch (Throwable $spock_ex) { this.getSpecificationContext().setThrownException($spock_ex) } finally { } this.thrownImpl(null, null, Exception) this.getSpecificationContext().getMockController().leaveScope()}

13年3月9日土曜日

Page 19: Spockの基礎

where:expect:expected == actualwhere:actual | expected1 | 1"a" | "a"

@FeatureMetadata(parameterNames = ['actual', 'expected'], ...)public void $spock_feature_0_0(Object actual, Object expected) { ...}

@DataProviderMetadata(line = -1, dataVariables = ['actual'])public Object $spock_feature_0_0prov0() { return [1, 'a']}

@DataProviderMetadata(line = -1, dataVariables = ['expected'])public Object $spock_feature_0_0prov1() { return [1, 'a']}

13年3月9日土曜日

Page 20: Spockの基礎

Mock()given:def foo = Mock(Foo)when:foo.add(1, 2)then:1 * foo.add(_, _)

Object foo = this.MockImpl('foo', null, Foo)this.getSpecificationContext().getMockController().enterScope()this.getSpecificationContext().getMockController().addInteraction( new InteractionBuilder(16, 9, '1 * foo.add(_, _)') .setFixedCount(1) .addEqualTarget(foo) .addEqualMethodName('add') .setArgListKind(true) .addEqualArg(_) .addEqualArg(_) .build())foo.add(1, 2)this.getSpecificationContext().getMockController().leaveScope()this.getSpecificationContext().getMockController().leaveScope()

13年3月9日土曜日

Page 21: Spockの基礎

あとは各自見ておくように!

13年3月9日土曜日

Page 22: Spockの基礎

AST変換の確認方法

⌘T

13年3月9日土曜日

Page 23: Spockの基礎

Specification

13年3月9日土曜日

Page 24: Spockの基礎

Specification

@RunWith(Sputnik.class)public abstract class Specification extends MockingApi { ...}

13年3月9日土曜日

Page 25: Spockの基礎

Sputnik

• org.junit.runner.Runnerの実装

• SpecInfoBuilder使ってSpecInfoを構築

• ExtensionRunner/ParameterizedSpecRunnerを使ってテスト実行

13年3月9日土曜日

Page 26: Spockの基礎

SpecInfopublic class SpecInfo extends ... { List<FieldInfo> fields List<IMethodInterceptor> setupInterceptors List<IMethodInterceptor> cleanupInterceptors List<IMethodInterceptor> setupSpecInterceptors List<IMethodInterceptor> cleanupSpecInterceptors List<IMethodInterceptor> sharedInitializerInterceptors List<IMethodInterceptor> initializerInterceptors

List<IRunListener> listeners

MethodInfo initializerMethod MethodInfo sharedInitializerMethod List<MethodInfo> setupMethods List<MethodInfo> cleanupMethods List<MethodInfo> setupSpecMethods List<MethodInfo> cleanupSpecMethods

List<FeatureInfo> features}

13年3月9日土曜日

Page 27: Spockの基礎

SpecInfoBuilder#doBuild()private SpecInfo doBuild() { buildSuperSpec(); buildSpec(); buildFields(); buildFeatures(); buildInitializerMethods(); buildFixtureMethods(); return spec;}

private void buildSuperSpec() { ... SpecInfo superSpec = new SpecInfoBuilder(superClass, clazz).doBuild(); ...}

13年3月9日土曜日

Page 28: Spockの基礎

super.setup()しないclass SubSpec extends BaseSpec { def setup() { super.setup() ... }}

SubSpec.groovy: 9: A base class fixture method should not be called explicitly because it is always invoked automatically by the framework @ line 9, column 9. super.setup() ^

1 error

13年3月9日土曜日

Page 29: Spockの基礎

ExtensionRunner

• org.junit.runner.Runnerの実装ではない

• フィーチャーメソッドの前にExtension(Global/AnnotationDriven)を実行

13年3月9日土曜日

Page 30: Spockの基礎

JUnit互換?

• @Rule/@ClassRuleはRuleExtensionによって実行される

• @Before/@Afterとかフィクスチャーメソッドとして扱われる

13年3月9日土曜日

Page 31: Spockの基礎

ParameterizedSpecRunner

• パラメータごとにフィーチャーメソッドを実行

• パラメータライズじゃないフィーチャーメソッドもこれで実行

13年3月9日土曜日

Page 32: Spockの基礎

まとめ

13年3月9日土曜日

Page 33: Spockの基礎

何得?

• AST変換の勉強にはなる

• Extensionを作るときに知ってるといい?

• 他のExtension実装を見たほうがはやい

13年3月9日土曜日