euphonictechnologies’s diary

Haskell超初心者の日記です。OCamlが好きです。

follow us in feedly

Haskellで作ったものすごく簡単なスペル修正プログラムを評価してみる - その1:IntelliJ上でHUnitを使ってスペル修正の評価テストを書く準備

前回はひとまずスペル修正プログラムが完成して、コマンドラインから修正したい単語の引数を与えるとその修正した単語と修正候補のトップ5が表示されるようにしてみた。

今回はスペル修正プログラムはどう書くかに従って、このスペル修正プログラムの評価ができるようなコードを書いていきたい。ユニットテストフレームワークを使って修正の正解率に対してテストを書くようなイメージでいる。

ユニットテストIntelliJ + Haskell環境でうまく動くようにする

今回はHUnitを使って、IntelliJ + Haskell環境でユニットテストが動くようにしたい。 まずは、HUnitを使えるようにcabalでパッケージをインストールする。

cabal install hunit
cabal install test-framework
cabal install test-framework-hunit

alexがインストール出来ない場合はgccのバージョンが不適合な場合があるのでcabal install --reinstall alexして再インストールすると直ることがある。

テストが出来るようにしていく:テスト用のダミーコードをつくる

基本的には

cabal test

とすると

Re-configuring with test suites enabled. If this fails, please run configure
manually.
Resolving dependencies...
Configuring spllerE-1.0...
Building spllerE-1.0...

という風に本来必要なcabal configure --enable-testcabal buildをやってからテストの実行をやってくれる、が

Package has no test suites.

と言われてしまう。当然テストに必要な物は何も足していないので、何もテストしない。これを解決していく。 必要なことは

  • srcディレクトリの下にtestディレクトリを作ってその中にテスト用のバイナリのMain.hsを作る。
  • そのMain.hsでテストするために.cabalにテスト用のエントリを足す

の2つ。

まずはスペル修正とは関係ないダミーのプログラムを書いてsrc/test/Main.hsに入れよう。

testの下にMainという名前のモジュールを追加する。module test.Main whereとなるかもしれないが、test.は不要。以下の様なダミーのテストを書く。

module Main where
    import Test.HUnit
    import Test.Framework
    import Test.Framework.Providers.HUnit

    main = do
        defaultMain $ hUnitTestToTests $ TestLabel "hoge" $ TestCase assertOne

    assertOne = do
        10 @=? sum [1,2,3,4]
        24 @=? product [1,2,3,4]
        "hoge" @=? "HOGE"

これがテスト。JUnitに似た雰囲気を感じ取ってもらえると思う。つまり、

  • HUnitのファイルは単なるモジュール。普通にプログラムとしてコンパイルできる。
  • 書き方が関数型言語っぽいが、IOモナドを使うことで順番に実行する手続き型言語の雰囲気を醸し出す
  • assertOneという関数を作って、その中で複数のことをassertしている。Assertは関数名としてassertEqualとかJavaっぽい関数で使うこともできるし、中置演算子で行うこともできる。

内容は後で深く追うとして、この内容でとりあえずセーブ。つぎはcabalファイルを編集してこれをビルドできるようにする。

テストが出来るようにしていく:.cabalファイルにテスト用のエントリを追加する

テストをするために、つまりcabal testで走らせるためにはTest-Suiteエントリが必要。内容は以下の感じ

Test-Suite Test
  type:       exitcode-stdio-1.0
  main-is:    Main.hs
  hs-source-dirs:  src/test
  build-depends:    base,HUnit,test-framework,test-framework-hunit
  ghc-options:     -Wall -O2

executableと雰囲気が似ている。typeというのにexitcode-stdio-1.0と書いてあって、このexitcode-stdio-1.0はプログラムの終了コードでテストの成否をcabalに伝えるtypeである、ということを指示している。他はexecutableと特に変わらない。コードで使うパッケージはHUnitのものとtest-frame-workというこのexitcode-stdio-1.0で終了コードをきちんと吐き出すために必要なフレームワークを追加した。

これでcabal testをしてみよう。

$ cabal build
Building spllerE-1.0...
Preprocessing executable 'spllerE' for spllerE-1.0...
Preprocessing test suite 'Test' for spllerE-1.0...

src/test/Main.hs:3:12:
    Could not find module `Test.Framework'
    It is a member of the hidden package `test-framework-0.8.0.3'.
    Perhaps you need to add `test-framework' to the build-depends in your .cabal file.
    Use -v to see a list of the files searched for.
Kodamas-MacBook-Air:~/Documents/OneDrive/IdeaProject/spllerE$ cabal test
Building spllerE-1.0...
Preprocessing executable 'spllerE' for spllerE-1.0...
Preprocessing test suite 'Test' for spllerE-1.0...
Running 1 test suites...
Test suite Test: RUNNING...
:hoge: [Failed]
expected: "hoge"
 but got: "HOGE"

         Test Cases  Total
 Passed  0           0
 Failed  1           1
 Total   1           1
Test suite Test: FAIL
Test suite logged to: dist/test/spllerE-1.0-Test.log
0 of 1 test suites (0 of 1 test cases) passed.

うまく動いているみたい。文字列の比較がわざと間違っているので、これで正解。

ここまで超走りながら来たけど、とりあえずsrc/test/Main.hsにテストを書いていけばcabal testできるようになった。仕組みは後で追いかけることにする。

IntelliJからユニットテストを走らせたい

コマンドラインからcabal testはだるい。IntelliJのRun configurationを定義してクリック一発でテストできるようにしてみる。

RunメニューのEdit Configurations...から新しいRun configurationをつくる。

中身はこんな感じ:

f:id:euphonictechnologies:20140810014906p:plain

まずは上のように既存のRun configurationと同じものを作る。executableがTestなところとBefore launchのところが違う。Before launchでcabal testを呼び出すようにしてみた。これだとcabal testした後に、Test自体を実行してテストを実行してしまう。すこし冗長だけど、とりあえずこれで行ってみる。 cabal testをBefore launchするために以下のようなExternal commandをつくってみた:

f:id:euphonictechnologies:20140810015117p:plain

これで"spllerE test"をRunしてみると(spllerEは前回まで作ってきたspllerと全く同じものの別名だ):

f:id:euphonictechnologies:20140810015248p:plain

と、cabal testと全く同じものが現れた。成功だ。

というわけで今回はここまで

残念、評価するところまで行かなかった。ただテストフレームワークが手に入ったのでsrc/test/Main.hsをがしがし書いていける。ここに評価に必要ないろいろなものをがしがし書いていきたい。

今回はいろいろなところを参考に以下まで辿り着いた。参考までに:

HaskellのUnitTest、HUnitについて学ぶ - エンジニアのソフトウェア的愛情

本物のプログラマはHaskellを使う - 第57回 機能テストや性能テストをCabalで自動化:ITpro

本物のプログラマはHaskellを使う - 第57回 機能テストや性能テストをCabalで自動化:ITpro

Cabal User Guide: Developing Cabal packages