2012年4月8日日曜日

#junitbc JUnit Boot Camp に参加してきた

最近、burnを飲み始めました。

みけです。

いきさつ


私のATND notifier には単語「JUnit」が登録されているので、

誰かがJUnitというキーワードのあるイベントが登録すると自動通知するようになっています。

というわけで、参加してきました。

JUnit強化キャンプ


内容


非常に内容が濃いので、何回かにわけて報告しようと思います。

個人的にはJUnitの応用編が大変勉強になりました。

そして、主催の @shuji_w6e さんが実は熟練したGroovyistであることもわかりました。


Groovy…エ


ざっくりしたGroovyの紹介でした。

POGOをJavaで使う際に便利なアノテーションで

コンストラクタやtoStringなども自動生成できることを教えてもらいました。

例えば、このようなアノテーションを付与したクラス
import groovy.transform.TupleConstructor
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
@TupleConstructor
@EqualsAndHashCode
@ToString
class Dollar {
final double value
}
view raw Dollar.groovy hosted with ❤ by GitHub



は、このようにコンパイルされます。

import groovy.transform.EqualsAndHashCode as EqualsAndHashCode
import groovy.transform.TupleConstructor as TupleConstructor
import groovy.transform.ToString as ToString
@groovy.transform.TupleConstructor
@groovy.transform.EqualsAndHashCode
@groovy.transform.ToString
public class Dollar implements groovy.lang.GroovyObject extends java.lang.Object {
final private double value
private static org.codehaus.groovy.reflection.ClassInfo $staticClassInfo
public static transient boolean __$stMC
private transient groovy.lang.MetaClass metaClass
public static long __timeStamp
public static long __timeStamp__239_neverHappen1333850088031
public Dollar(double value) {
metaClass = /*BytecodeExpression*/
this .value = value
}
public Dollar() {
this (((0.0) as double))
}
public int hashCode() {
java.lang.Object _result = org.codehaus.groovy.util.HashCodeHelper.initHash()
_result = org.codehaus.groovy.util.HashCodeHelper.updateHash( _result , this.getValue())
return _result
}
public boolean canEqual(java.lang.Object other) {
return other instanceof Dollar
}
public boolean equals(java.lang.Object other) {
if ( other == null) {
return false
}
if (this.is(other)) {
return true
}
if ( other instanceof Dollar) {
} else {
return false
}
if (other.canEqual( this )) {
} else {
return false
}
java.lang.Object otherTyped = (( other ) as Dollar)
if (this.getValue() != otherTyped.getValue()) {
return false
}
return true
}
public java.lang.String toString() {
java.lang.Object _result = new java.lang.StringBuffer()
_result.append('Dollar')
_result.append('(')
_result.append(org.codehaus.groovy.runtime.InvokerHelper.toStringthis.getValue())
_result.append(')')
return _result.toString()
}
public java.lang.Object this$dist$invoke$1(java.lang.String name, java.lang.Object args) {
return this."$name"(* args )
}
public void this$dist$set$1(java.lang.String name, java.lang.Object value) {
this ."$name" = value
}
public java.lang.Object this$dist$get$1(java.lang.String name) {
return this ."$name"
}
protected groovy.lang.MetaClass $getStaticMetaClass() {
}
public groovy.lang.MetaClass getMetaClass() {
}
public void setMetaClass(groovy.lang.MetaClass mc) {
}
public java.lang.Object invokeMethod(java.lang.String method, java.lang.Object arguments) {
}
public java.lang.Object getProperty(java.lang.String property) {
}
public void setProperty(java.lang.String property, java.lang.Object value) {
}
public static void __$swapInit() {
}
static static {
__timeStamp__239_neverHappen1333850088031 = 0
__timeStamp = 1333850088031
}
final public double getValue() {
}
public void super$1$wait() {
}
public java.lang.String super$1$toString() {
}
public void super$1$wait(long param0) {
}
public void super$1$wait(long param0, int param1) {
}
public void super$1$notify() {
}
public void super$1$notifyAll() {
}
public java.lang.Class<java.lang.Object extends java.lang.Object> super$1$getClass() {
}
public java.lang.Object super$1$clone() {
}
public boolean super$1$equals(java.lang.Object param0) {
}
public int super$1$hashCode() {
}
public void super$1$finalize() {
}
}



toStringやコンストラクターが自動で生成されるんですね。



AppengineTestCase


JUnitの応用編で教えてもらったRuleアノテーションの利用方法です。

Slim3にてテストクラスを作成する場合は、AppengineTestCaseクラスを継承して作成するのが一般的です。

そこで、下記のようなユーティリティクラスを作成します。

package org.mikeneck.gae.slim3.sample.util;
import org.junit.rules.ExternalResource;
import org.slim3.tester.AppEngineTester;
/**
* @author : mike
* @since : 12/04/08
*/
public class AppengineExternalResource extends ExternalResource {
private AppEngineTester tester = new AppEngineTester();
@Override
protected void after() throws RuntimeException{
try {
tester.tearDown();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
protected void before() throws Throwable {
tester.setUp();
}
}



これは、単純にAppengineTestCaseクラスを真似したようなクラスです。

このユーティリティクラスにRuleアノテーションを付与して次のようにテストクラスを作成します。

package org.mikeneck.gae.slim3.sample;
import com.google.appengine.api.datastore.Key;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.mikeneck.gae.slim3.sample.util.AppengineExternalResource;
import org.slim3.datastore.Datastore;
import java.util.Date;
import java.util.List;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
/**
* @author : mike
* @since : 12/04/08
*/
public class MessageTest {
@Rule
public static ExternalResource resource = new AppengineExternalResource();
private static final MessageMeta meta = MessageMeta.get();
@Test
public void countData () {
Message message = new Message();
message.setCreatedAt(new Date());
message.setMessage("test");
Key key = Datastore.put(message);
Message stored = Datastore.get(meta, key);
assertThat(stored.getMessage(), is("test"));
}
}



すると、不思議なことにAppengineTestCaseを継承していなくてもテストクラスが作成できます。

これのお陰でBeforeアノテーションを付与したsetUpメソッドを別途作成することができ、super.setUp()を呼び出さなくても良くなります。

なお、ものにもよりますが、Ruleアノテーションを付与したExternalResourceのbeforeメソッドはBeforeアノテーションより前に実行されます。


明日、また内容についてブログを書きます。

それでは。



参考

junitにデフォルトであるExternalResourceクラス

package org.junit.rules;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* A base class for Rules (like TemporaryFolder) that set up an external
* resource before a test (a file, socket, server, database connection, etc.),
* and guarantee to tear it down afterward:
*
* <pre>
* public static class UsesExternalResource {
* Server myServer= new Server();
*
* &#064;Rule
* public ExternalResource resource= new ExternalResource() {
* &#064;Override
* protected void before() throws Throwable {
* myServer.connect();
* };
*
* &#064;Override
* protected void after() {
* myServer.disconnect();
* };
* };
*
* &#064;Test
* public void testFoo() {
* new Client().run(myServer);
* }
* }
* </pre>
*/
public abstract class ExternalResource implements TestRule {
public Statement apply(Statement base, Description description) {
return statement(base);
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
} finally {
after();
}
}
};
}
/**
* Override to set up your specific external resource.
* @throws if setup fails (which will disable {@code after}
*/
protected void before() throws Throwable {
// do nothing
}
/**
* Override to tear down your specific external resource.
*/
protected void after() {
// do nothing
}
}


statementメソッドでbefore()→evaluate()→after()の順番で実行されています。

このevaluate()にて@Before→@Test→@Afterが実行されていきます。



0 件のコメント:

コメントを投稿