Upload
kiyotaka-oku
View
3.694
Download
5
Embed Size (px)
DESCRIPTION
Citation preview
Spockの基礎@kiy0taka
13年3月9日土曜日
自己紹介
•奥 清隆(おく きよたか)• @kiy0taka、id:kiy0taka
•株式会社ニューキャスト所属•名古屋在住JGGUG関西支部長•仕事:Groovy/Grailsとか
13年3月9日土曜日
spock-shell
• シェルスクリプトのテストをSpockで書くSpock拡張
• http://git.io/spock-shell
13年3月9日土曜日
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日土曜日
Agenda
• AST変換
• Specification
13年3月9日土曜日
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日土曜日
SpecParser
• テストクラスからSpecを組み立てる
13年3月9日土曜日
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日土曜日
SpecRewriter
• テストクラスの原形がなくなるまで殴り続ける
• 仲間を呼んでボコボコにする
13年3月9日土曜日
Rewriter連合
• SpecRewriter
• ConditionRewriter
• DeepBlockRewriter
• InteractionRewriter
• WhenBlockRewriter
13年3月9日土曜日
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日土曜日
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日土曜日
フィールドdef date = new Date()
@FieldMetadata(name = 'date', line = 8, ordinal = 0)private Object date
private Object $spock_initializeFields() { date = new Date()}
13年3月9日土曜日
@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日土曜日
フィーチャー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日土曜日
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日土曜日
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日土曜日
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日土曜日
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日土曜日
あとは各自見ておくように!
13年3月9日土曜日
AST変換の確認方法
⌘T
13年3月9日土曜日
Specification
13年3月9日土曜日
Specification
@RunWith(Sputnik.class)public abstract class Specification extends MockingApi { ...}
13年3月9日土曜日
Sputnik
• org.junit.runner.Runnerの実装
• SpecInfoBuilder使ってSpecInfoを構築
• ExtensionRunner/ParameterizedSpecRunnerを使ってテスト実行
13年3月9日土曜日
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日土曜日
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日土曜日
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日土曜日
ExtensionRunner
• org.junit.runner.Runnerの実装ではない
• フィーチャーメソッドの前にExtension(Global/AnnotationDriven)を実行
13年3月9日土曜日
JUnit互換?
• @Rule/@ClassRuleはRuleExtensionによって実行される
• @Before/@Afterとかフィクスチャーメソッドとして扱われる
13年3月9日土曜日
ParameterizedSpecRunner
• パラメータごとにフィーチャーメソッドを実行
• パラメータライズじゃないフィーチャーメソッドもこれで実行
13年3月9日土曜日
まとめ
13年3月9日土曜日
何得?
• AST変換の勉強にはなる
• Extensionを作るときに知ってるといい?
• 他のExtension実装を見たほうがはやい
13年3月9日土曜日