JMockit の Mock 化する方法あれこれ

JMockit で java.util.Calendar のメソッドを Mock 化する - kntmr-blog

これの続き。

以下、テスト対象クラスとして単純に文字列を連結して返すだけのクラスを用意。

public class FooService {
    BarService barService;
    public String execute(String arg) {
        return "Foo" + barService.execute(arg);
    }
}

public class BarService {
    public String execute(String arg) {
        return "Bar" + arg;
    }
}

JMockit の Expectations で Mock 化する

モック化は MockUp<T> を使う方法以外に、Expectations を使う方法がある。

FooService#execute(String) をテストする場合。

public class FooServiceTest {
    @Tested
    FooService sut;
    @Injectable
    BarService barService;
    @Test
    public void test() {
        final String expected = "FooBARBAZ";
        new Expectations() {
            {
                barService.execute(anyString); result = "BARBAZ"; times = 1; // モックの挙動と呼出しの検証
            }
        };
        String actual = sut.execute("dummy");
        assertThat(actual, is(expected));
    }
}

Expectations はモックの挙動を定義するだけではなく、メソッド呼出しを検証することができる。
上記は times = 1BarService#execute(String) が1回だけ呼ばれることを検証する。

Verifications を使うと検証の部分を Expectations から分離することができる。

public class FooServiceTest {
    @Tested
    FooService sut;
    @Injectable
    BarService barService;
    @Test
    public void test() {
        final String expected = "FooBARBAZ";
        new Expectations() {
            {
                barService.execute(anyString); result = "BARBAZ"; // モックの挙動を定義
            }
        };
        String actual = sut.execute("dummy");
        assertThat(actual, is(expected));
        new Verifications() {
            {
                barService.execute(anyString); times = 1; // 呼出しの検証
            }
        };
    }
}

モックに渡した値を検証する場合。

public class FooServiceTest {
    @Tested
    FooService sut;
    @Injectable
    BarService barService;
    @Test
    public void test() {
        sut.execute("hogehoge");
        new Verifications() {
            {
                List<String> arg = new ArrayList<String>();
                barService.execute(withCapture(arg)); times = 1;
                assertThat(arg.get(0), is("hogehoge"));
            }
        };
    }
}

withCapture でモックに渡した値をチェックすることができる。

JMockit で private メソッドを Mock 化する

以下、文字列の連結部分を private メソッドに切り出しただけのクラス。

public class FooService {
    public String execute(String arg) {
        return executeInternal(arg);
    }
    private String executeInternal(String arg) {
        return "Foo" + arg;
    }
}

FooService#executeInternal(String) をテストする場合。

public class FooServiceTest {
    @Tested
    FooService sut;
    @Test
    public void test() {
        final String expected = "FooBar";
        String actual = Deencapsulation.invoke(sut, "executeInternal", "Bar");
        assertThat(actual, is(expected));
    }
}

Deencapsulation#invoke(Object, String, Object...) にテスト対象クラスとメソッド名、パラメータを渡して private メソッドを実行する。

Haskell を使ってみる 3 (リスト内包表記)

前回の続き

Haskell を使ってみる 2 (リストの操作) - kntmr-blog

レンジ

列挙できる要素の組み合わせでリストを作成することができる。

Prelude> [1..10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"

ステップを指定してリストを作成することができる。ステップサイズは1つしか指定できないため、等差数列ではないリストは作成できない。

Prelude> [2,4..10] -- 偶数のリスト
[2,4,6,8,10]
Prelude> [10,9..1] -- 減少列の作成が可能
[10,9,8,7,6,5,4,3,2,1]

Prelude> ['a','c'..'z'] -- 文字列のステップ指定
"acegikmoqsuwy"

レンジで上限を指定しない場合、無限リストが作成できる。Haskell は無限リスト全体をすぐに評価することはしない。(遅延評価なので)

Prelude> take 10 [0,5..] -- 無限リストのうち最初の10個の要素で構成されるリストを作成
[0,5,10,15,20,25,30,35,40,45]

cycle は指定されたリストの要素を無限に繰り返して無限リストを作成する。

Prelude> take 10 (cycle [1,2,3]) -- 無限リストのうち最初の10個の要素で構成されるリストを作成
[1,2,3,1,2,3,1,2,3,1]

repeat は指定された要素を無限に繰り返して無限リストを作成する。

Prelude> take 10 (repeat 1) -- 無限リストのうち最初の10個の要素で構成されるリストを作成
[1,1,1,1,1,1,1,1,1,1]

replicate は繰り返す回数と要素を指定してリストを作成する。

Prelude> replicate 5 1
[1,1,1,1,1]

レンジで浮動小数点を扱う場合は精度に注意する。

Prelude> [0.1,0.3..1]
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]

リスト内包表記

[1..10] で生成したリストの各要素の値を x束縛する。| より左はリスト内包表記の出力を表す。

Prelude> take 10 [2,4..] -- 単純なリスト操作
[2,4,6,8,10,12,14,16,18,20]
Prelude> [x * 2 | x <- [1..10]] -- リスト内包表記
[2,4,6,8,10,12,14,16,18,20]

| より右には条件 (述語) を追加できる。述語でリストを間引くことをフィルタと呼ぶ。カンマ区切りで複数の述語を指定したり、複数のリストから値を取り出すことができる。

Prelude> [x * 2 | x <- [1..10], x * 2 > 10] -- 述語を指定
[12,14,16,18,20]
Prelude> [x + y | x <- [1,2,3], y <- [10,100,1000]] -- 複数のリストから値を取り出す
[11,101,1001,12,102,1002,13,103,1003]
Prelude> [x + y | x <- [1,2,3], y <- [10,100,1000], x + y > 100] -- 述語を追加
[101,1001,102,1002,103,1003]

リスト内包表記を使って length 関数を定義する。リストの要素をすべて 1 に置換して sum 関数で足し合わせる。リストから取り出した要素は使わないので _ (アンダースコア) で使い捨てる。

Prelude> length' xs = sum [1 | _ <- xs]
Prelude> length' [1,2,3,4,5]
5
Prelude> length' "Hello"
5

リスト内包表記を使って文字列をフィルタする関数を定義する。

Prelude> removeLowercase str = [c | c <- str, c `elem` ['A'..'Z']]
Prelude> removeLowercase "Hello Haskell"
"HH"

リスト内包表記の入れ子のリストを操作する。外側のリスト内包表記が出力が、別のリスト内包表記になっている。リスト内包表記の出力は何らかのリストになる。

Prelude> let xxs = [[1,2,3,4],[11,12,13,14],[21,22,23,24]]
Prelude> [[x | x <- xs, even x] | xs <- xxs]
[[2,4],[12,14],[22,24]]

今回はリスト内包表記まで。

単体テスト(ユニットテスト/UT)で考えること

単体テスト(ユニットテスト/UT)で考えることを雑に書きます。


コードを書くときはテストしやすい粒度でメソッドを分割する。

以下の場合、なにも考えないと execute 内にすべての処理を書きがち。1メソッドの処理が長くなるとテストがやり辛くなる。

これをある程度まとまりのある処理で foo, bar などにメソッドを分割して execute から呼び出すようにする。そして、foo, bar に対してテストを書くようにすればテストコードにまとまりができて構造化し易くなる。 また、分割したメソッドのアクセス修飾子はパッケージプライベートにして、テストクラスは同一のパッケージに配置する。

public void execute(String arg) {
    Hoge hoge = foo(arg);
    Fuga fuga = bar(hoge);
}

Hoge foo(String arg) {
    // ...
}

Fuga bar(Hoge hoge) {
    // ...
}

ただ、メンバーのスキルだったり開発環境の要因でテストコードが十分に準備できないことがあるかもしれないので、必ずテストコードを書くということは考えない。
(そこを無理してやろうとすると、コストが膨れ上がるリスクがある気がするので無理はしたくない)

こういうときは人手でテストすることになるが、当然自動化みたいなことはできないので観点を挙げてテストすることが重要。
状況によってはスタブなどは事前に用意する必要はある。

余談

Java ではクラスやメソッド、変数に日本語 (Unicode) が使えます。まぁ、実際に使うことはないだろうと思っていましたが、『JUnit実践入門 - 体系的に学ぶユニットテストの技法』ではメソッドが日本語で書かれています。(最初、見たときは目から鱗だった…)

日本語なら書きやすいと思って「引数に xxx を指定した場合は hoge を返すこと」のようにメソッド名を付けるわけですが、日本語ですらどんな名前を付けようか迷うことがあります。

大抵、こういう場合は処理内容が複雑になり過ぎていることがあるので、リファクタリングすることを検討します。

これはテストに限った話ではなく、普段からコードを書くときに命名に迷うことがあればどこか構造的におかしい部分がある、と考えるようにしています。

あと、割と昔から JUnitJMockit の組み合わせ使っていますが、逆に言うとこれ以外はあまり使ったことがない…。

JMockit で java.util.Calendar のメソッドを Mock 化する

必要に迫られて調べました。

以下は java.util.Calendar#getTimeInMillis の場合。

final long expected = 1234567890000L;
new MockUp<Calendar>() {
    @Mock
    public long getTimeInMillis() {
        return expected;
    }
};
long actual = Calendar.getInstance().getTimeInMillis(); // 1234567890000 が返る
assertThat(actual, is(expected));

他のメソッドも同じように Mock 化できるはず。

ちなみに

おそらく周知の事実とは思いますが、JMockitJUnit よりも先に読み込まれるように classpath を設定する必要があります。

プリミティブのラッパーのコンストラクタの代わりに valueOf を使う件

valueOfインスタンスを取得することになるわけか。

というわけで、Integer#valueOf(int) を見てみます。(これは Java 8 ですが)

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

そういえばどこかで「valueOf メソッドはキャッシュしているインスタンスを返す」という情報を読んだことがあったような気がする。デフォルトでは [-128, 127] の範囲の値をキャッシュするようです。(-XX:AutoBoxCacheMax オプションで変更可能)

以下、簡単にですが試してみます。

当然、以下は false になります。

Integer constructor = new Integer(100);
Integer valueOf = Integer.valueOf(100);
assertThat((constructor == valueOf), is(false));

以下はキャッシュの範囲内なので、同一のインスタンスが返ります。よって true になります。

Integer valueOf1 = Integer.valueOf(100);
Integer valueOf2 = Integer.valueOf(100);
assertThat((valueOf1 == valueOf2), is(true));

以下も同様。

Integer valueOf1 = Integer.valueOf(127);
Integer valueOf2 = Integer.valueOf(127);
assertThat((valueOf1 == valueOf2), is(true));

以下も同様。

Integer valueOf1 = Integer.valueOf(-128);
Integer valueOf2 = Integer.valueOf(-128);
assertThat((valueOf1 == valueOf2), is(true));

以下はキャッシュの範囲外なので、新しくインスタンスを生成して返します。よって false になります。

Integer valueOf1 = Integer.valueOf(128);
Integer valueOf2 = Integer.valueOf(128);
assertThat((valueOf1 == valueOf2), is(false));

以下も同様。

Integer valueOf1 = Integer.valueOf(-129);
Integer valueOf2 = Integer.valueOf(-129);
assertThat((valueOf1 == valueOf2), is(false));

なるほど。

Haskell を使ってみる 2 (リストの操作)

前回の続き

Haskell を使ってみる 1 (導入) - kntmr-blog

リスト同士の連結

++ 演算子は2つのリストを引数に取る。第1引数に指定したリストは最後まで走査されるため、要素数が大きいリストを扱う場合は注意が必要。

Prelude> [1,2,3,4] ++ [5,6,7,8]
[1,2,3,4,5,6,7,8]
Prelude> "Hello" ++ " " ++ "Haskell" -- 文字列は文字のリストとして表される
"Hello Haskell"

Prelude> [1,2,3,4] ++ 5 -- これはエラー

<interactive>:20:1: error:
    • Non type-variable argument in the constraint: Num [a]
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall a. (Num [a], Num a) => [a]

Prelude> [1,2,3,4] ++ [5] -- これはOK
[1,2,3,4,5]

リストの先頭に要素を追加

: 演算子の第1引数は、追加するリストの要素と同じ型の単一の要素。

Prelude> 4 : [1,2,3]
[4,1,2,3]
Prelude> 'x' : "abcde"
"xabcde"

リストの要素にアクセス

Prelude> [1,2,3,4,5] !! 1
2
Prelude> "Hello Haskell" !! 4
'o'

リスト同士の比較

先頭の要素から辞書順に比較される。

Prelude> [1,2,3] < [1,2,5]
True
Prelude> ['a','b','x'] < ['a','b','c']
False
Prelude> "Hello" == "Hello"
True

その他のリスト操作

Prelude> head [1,2,3,4,5]
1
Prelude> tail [1,2,3,4,5]
[2,3,4,5]
Prelude> last [1,2,3,4,5]
5
Prelude> init [1,2,3,4,5]
[1,2,3,4]
Prelude> head [] -- 空のリストでは使えない
*** Exception: Prelude.head: empty list

Prelude> length [1,2,3,4,5]
5
Prelude> null [1,2,3,4,5] -- リストが空かどうか
False
Prelude> null []
True

Prelude> reverse [1,2,3,4,5]
[5,4,3,2,1]

Prelude> take 3 [1,2,3,4,5] -- 先頭から指定された数の要素を取り出してリストで返す
[1,2,3]
Prelude> drop 2 [1,2,3,4,5] -- 先頭から指定された数の要素を除いたリストを返す
[3,4,5]

Prelude> maximum [3,2,5,1,4] -- 辞書順で最も大きい要素を返す
5
Prelude> minimum [3,2,5,1,4] -- 辞書順で最も小さい要素を返す
1

Prelude> sum [1,2,3,4,5] -- 要素の和を返す
15
Prelude> product [1,2,3,4,5] -- 要素の積を返す
120

Prelude> elem 2 [1,2,3,4,5] -- 指定された要素がリストに含まれているか
True
Prelude> elem 9 [1,2,3,4,5]
False

elem は中置関数として用いられることが多い。

Prelude> 3 `elem` [1,2,3,4,5]
True

今回はリストの操作まで。

Elasticsearch を使ってみる on Windows

とりあえずメモ。

前提

  • Windows 7 Professional SP1 64bit
  • Elasticsearch 2.3.4

インストール/起動

Download Elasticsearch Free • Get Started Now | Elastic

  1. ZIP をダウンロードして展開
  2. bin/elasticsearch.bat を実行

使ってみる

GitHub - elastic/elasticsearch: Open Source, Distributed, RESTful Search Engine

↑のチュートリアルを参考に使ってみる。

尚、すべてのツイートを検索するには /twitter/tweet/_search"match_all":{} のクエリでリクエストするように書いてあるが、単純に GET するだけでよさそう。

elasticsearch-head

Elasticsearch の GUI コンソール

  1. bin/plugin.bat install mobz/elasticsearch-head を実行
    (プロキシ環境の場合は -DproxyHost=hostname -DproxyPort=port を付ける)
  2. ブラウザから http://localhost:9200/_plugin/head/ にアクセス