JavaのライブラリのテストをJRuby+minitestで書く

お前は何と戦っているんだ感が半端ないですが、タイトルの通りJavaで書いたライブラリのテストをJRuby+minitestで書いてみました。

テストを書く対象のライブラリは、よくある(?)行列演算を行うやつです。 例としてMyMatrixLib.jar (net.rkmathi.lib.MyMatrixLib)という名前にしてみます。

JRuby, minitestのインストール

rbenvを使っているので、それ経由で入れました。

$ rbenv install -l | grep jruby
...
jruby-1.7.16
jruby-1.7.16.1
jruby-9.0.0.0-dev
jruby-9000-dev
...

jruby-1.7.16.1にしました。

$ rbenv install jruby-1.7.16.1

テスト書くディレクトリに移動して、rbenv localjrubyを使うようにします。

$ cd /path/to/test_directory
$ rbenv local jruby-1.7.16.1
$ rbenv rehash
$ jruby --version
jruby 1.7.16.1 (1.9.3p392) 2014-10-28 4e93f31 on Java HotSpot(TM) 64-Bit Server VM 1.8.0_25-b17 +jit [darwin-x86_64]

minitestのインストールをします。

$ jruby -S gem install minitest

ライブラリのJARを持ってくる

$ cd /path/to/test_directory
$ cp /path/to/MyMatrixLib.jar .

JRuby+minitestでテストを書く

とりあえず、get()とplus()とminus()のテストを書いてみるとこうなります。

require 'minitest'
require 'minitest/autorun'

require 'java'
require_relative './MyMatrixLib.jar'

import 'net.rkmathi.lib.MyMatrixLib'

class TestMyMatrixLib < Minitest::Test
  def setup
    @d1 = rand(1000.0)
    @d2 = rand(1000.0)
    @m1 = MyMatrixLib.new(3, 2, @d1)  # 3x2ですべての要素が@d1の行列を生成
    @m2 = MyMatrixLib.new(3, 2, @d2)  # 3x2ですべての要素が@d2の行列を生成
  end

  # get(r, c) => double: 行列のr行c列目の要素を取得
  def test_get
    each_assert_get @m1, @d1
    each_assert_get @m2, @d2
  end

  # plus(matrix) => MyMatrix: 行列のそれぞれの要素を足した行列を生成
  def test_plus
    d_res = @d1 + @d2
    m_res = @m1.plus(@m2)  # @m1と@m2のそれぞれの要素を足した結果の行列
    each_assert_get m_res, d_res
  end

  # minus(matrix) => MyMatrix: 行列のそれぞれの要素を引いた行列を生成
  def test_minus
    d_res = @d1 - @d2
    m_res = @m1.minus(@m2)  # @m1と@m2のそれぞれの要素を引いた結果の行列
    each_assert_get m_res, d_res
  end

  private

  # 行列の各要素をそれぞれgetするのが面倒なのでメソッド化
  def each_assert_get(m, d)
    for r in 0...m.get_row_dimension do
      for c in 0...m.get_column_dimension do
        assert_equal m.get(r, c), d
      end
    end
  end

  ...

end

と、こんな感じで書くことができます。

ちなみにJUnitだと...

public class MyMatrixLibTest {
    @Before
    public void setUp() throws Exception {
        MyMatrixLib d1 = Math.random() * 1000.0;
        MyMatrixLib d2 = Math.random() * 1000.0;
        MyMatrixLib m1 = new MyMatrixLib(3, 2, d1);
        MyMatrixLib m2 = new MyMatrixLib(3, 2, d2);
    }

    @Test
    public void testGet() {
        eachAssertGet(m1, d1);
        eachAssertGet(m2, d2);
    }

    @Test
    public void testPlus() {
        double d_res = d1 + d2;
        MyMatrixLib m_res = m1.plus(m2);
        eachAssertGet(m_res, d_res);
    }

    @Test
    public void testMinus() {
        double d_res = d1 - d2;
        MyMatrixLib m_res = m1.minus(m2);
        eachAssertGet(m_res, d_res);
    }

    private void eachAssertGet(MyLibMatrix m, double d) {
        for (int row = 0; row < m.getRowDimension(); row++) {
            for (int column = 0; column < m.getRowDimension(); column ++) {
                assertEquals(m.get(row, column), d);
            }
        }
    }
}

簡単な演算のテスト部分しか比較しませんでしたが、書いてて楽には思えました。

ですが、JRubyを書いていると文法とか変数の型をミスった時に吐かれる例外がよくわからないことになることもあるので、わざわざJRubyでテスト書くのは微妙かも...