2013年5月20日月曜日

『JUnit実践入門』写経・実践会 in 横浜 #6 (特別編) に参加してきました #junitbook

『JUnit実践入門』写経・実践会 in 横浜 #6 (特別編)
http://connpass.com/event/2270/

2013/05/12 『JUnit実践入門』写経・実践会 in 横浜 #6 (特別編) #junitbook
http://togetter.com/li/490112

はじめに

前回の写経・実践会のレポートはこちら→『JUnit実践入門』写経・実践会 in 横浜 #5 に参加してきました #junitbook
今回は、@irofさんの「テスト駆動開発を継続する」と、著者の@shuji_w6eさんのJUnit実践入門には含まれなかった幻の章についての講演とハンズオンでした。

テスト駆動開発を継続する



テストの価値は失敗することにあるというのは、現在の状況と目的が一致している状態から変化があったことを分かりやすくしたものだと思いました。
アサーションを分かりやすく一つづつ実施することで、どの状態が意図している目的で、どうして目的に合致しなかったのか見つけやすくなるのかなと思います。
大きなオブジェクトの状態を確認しようとするとアサーションの行が増えますし、本当に確認したいことが埋もれてしまっては、元も子もないと思います。
設計を見なおしたり、リファクタリングをする際に、目的の状態になっていることを確認するためのテストを書き、その状態を維持しつつ小さく分かりやすいテストにしていくことが重要なことだと感じました。
そういった小さいテストを継続的に積み上げることで、ひとつの変更が影響する範囲を視覚的に捉えることが可能になるのではないかなと、思います。
小さいテストであれば、わざと目的通りのRedにした場合でも、意図している動作であるか確認しやすいのではと思いました。

ハンズオン

後半は、@shuji_w6eさんの「受け入れテストの自動化とユースケース駆動開発 -Pre Cucumber Boot Camp-」と「cucumber + selenium」による受け入れテストのハンズオンでした。

元々はmavenとeclipseを対象にしたプロジェクトでしたが、IntelliJ IDEAとGradleでやってみようと思い、前日にGradle 1.6のBuild Setup Pluginを利用してbuild.gradleを作っておきました。

apply {
plugin 'java'
plugin 'war'
plugin 'jetty'
}
apply plugin: 'maven'
apply {
plugin 'idea'
}
group = 'junitbook'
version = '0.0.1-SNAPSHOT'
description = "Book store"
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenCentral()
maven {
url 'http://download.eclipse.org/rt/eclipselink/maven.repo'
}
}
dependencies {
compile group: 'org.slf4j', name: 'slf4j-jdk14', version: '1.7.5'
compile group: 'org.tuckey', name: 'urlrewritefilter', version: '4.0.4'
compile group: 'org.eclipse.persistence', name: 'eclipselink', version: '2.4.1'
compile group: 'org.springframework', name: 'spring-webmvc', version: '3.2.2.RELEASE'
compile group: 'org.springframework', name: 'spring-tx', version: '3.2.2.RELEASE'
compile group: 'org.springframework', name: 'spring-orm', version: '3.2.2.RELEASE'
compile group: 'aopalliance', name: 'aopalliance', version: '1.0'
compile group: 'cglib', name: 'cglib', version: '2.2'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.6.10'
compile group: 'org.hibernate', name: 'hibernate-validator', version: '5.0.0.Final'
compile group: 'org.thymeleaf', name: 'thymeleaf-spring3', version: '2.0.16'
compile group: 'com.h2database', name: 'h2', version: '1.3.171'
testCompile group: 'junit', name: 'junit', version: '4.11'
testCompile group: 'org.springframework', name: 'spring-test', version: '3.2.2.RELEASE'
testCompile group: 'org.dbunit', name: 'dbunit', version: '2.4.9'
providedCompile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
providedCompile group: 'javax.servlet.jsp', name: 'jsp-api', version: '2.1'
testCompile 'org.seleniumhq.selenium:selenium-chrome-driver:2.31.0'
testCompile 'info.cukes:cucumber-java:1.1.3'
testCompile 'info.cukes:cucumber-junit:1.1.3'
}
buildscript {
ext {
h2Version = '1.3.171'
}
repositories {
mavenCentral()
}
dependencies {
classpath "com.h2database:h2:$h2Version"
}
}
ext {
databaseDir = "${projectDir}/h2/database"
h2Args = ['-baseDir', "${databaseDir}"]
databaseName = 'bookstore'
databaseSchemas = ['dev', 'ut']
}
task h2Start(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'org.h2.tools.Server'
args = h2Args
}
[jettyRun, jettyRunWar]*.with {
reload = 'automatic'
additionalRuntimeJars = sourceSets.main.runtimeClasspath
// httpPort = 9000
}
def h2TcpServerStart = { args ->
org.h2.tools.Server.createTcpServer(args as String[]).start()
}
def server
[jettyRun, jettyRunWar]*.doFirst { server = h2TcpServerStart(h2Args) }
[jettyRun, jettyRunWar]*.doLast { server.stop() }
task bookstoreInit {
doFirst {
mkdir databaseDir
}
server = h2TcpServerStart(h2Args)
def runSqlScript = { File script, schema = '' ->
def url = "jdbc:h2:${server.URL}/${databaseName};${ schema ? "SCHEMA=${schema}" : '' }"
// println url
org.h2.tools.RunScript.execute(url, "sa", "", script.path, 'UTF-8', false)
}
runSqlScript file('db_schema/initdb.sql')
databaseSchemas.each { schema ->
runSqlScript file('db_schema/setup.sql'), schema
}
}
view raw build.gradle hosted with ❤ by GitHub


無事、IntelliJ IDEAでもcucumberを使えるようになりました。
また、h2databaseやjettyについてもtaskを追加しておいたお陰で、コマンドを実行するだけで起動や初期化が出来るようになりました。

途中でフィーチャファイルの補完が効いてないのかなと思いました。

が、IntelliJ IDEAを12.1.2から12.1.3に上げて見たところ、シンタックスハイライトとコードジャンプも出来るようになりました。
さすがJetBrains様です。

なんとか、ハンズオンの進行に付いていくことが出来ましたし、seleniumの残念な箇所も体験することが出来ました。
前回のBDDにてイマイチ使い所が分からなかったcucumberも、seleniumと組み合わせることでどうやって使ったら良いのか体験することが出来たのは良かったと思いました。

おわりに

懇親会では、@enumさんも参加して下さり、とても楽しい充実した時間でした。

今回のハンズオンは、プレと冠していましたがとても実践的でスタートラインまで連れて行ってもらえる内容でした。
今後は、どうやってシナリオを書いて行ったら良いのかなど、教えていただいた書籍を参考にやってみようと思います。

また、GroovyではGebがあるので、これとSpockを組み合わせることで、seleniumの残念な箇所をカバーしてくれて、なおかつシナリオもコードで書けるようになるので、同じ課題を挑戦してみたいと思います。

講師の@irofさん、著者の@shuji_w6eさん、主催の@shinyaa31さん、参加者の皆さん、会場の横浜タネマキさん、ありがとうございました。

残りの章も少なくなって来ましたが、次回もよろしくお願いします。