Persimmon用のランダムテストライブラリ案(もしくは scalacheck の F# 移植)
あけましておめでとうございます。
さっそくですが進捗どうですか? 私は駄目でした(Persimmon用VS Test Explorer拡張的な意味で)。
今回はランダムテスト実装の話です。
Persimmon と FsCheck の相性について
FsCheck には IRunner という、拡張ポイントがあります。
この型を実装して Config
に設定することで、実装した各メソッドがコールバックされます。
が、これは このコメント にもある通り、xUnit.NET や NUnit ぽいものとは相性が良いものの、型を欲する Persimmon とは若干相性が良くないです。
では、 Runner だけ自力実装…というのも考えたのですが、断念しました。
実行に必要そうないくつかのモジュールや型が internal
になっており、互換を崩してまであのエグい実装をコピーしてメンテしたいかというと、うーん…。
FsCheck の公開関数が unit
を返す関数ばかりなのは何かの呪いですかね…。
そんなこんなで、FsCheck との連携は使いやすいかなんともいえないので、代替案のライブラリを作ってみようと思ったのが昨年末の話です。
干し柿(という名の scalacheck 移植)
というわけで、大晦日あたりから本命の息抜きに作ってみました。
persimmon-projects/Persimmon.Dried · GitHub
この干し柿は scalacheck をベースにしています。 手を入れるうちにいろいろ変わっていくと思うので、現時点では、という注釈つきですが。
FsCheck と scalacheck がどの程度異なるかは下記記事を参考にしてみてください。
FsCheck と ScalaCheck とを見比べる — a wandering wolf
以下、ちょっとした違いについて書いてみます。
Arbitrary
scalacheck の forAll
などは型クラスを用いて Arbitrary
インスタンスや Shrink
インスタンスをコンパイル時に補完します。
なので、別に Arbitrary
が Gen
しかもっていなくても不都合が起きません。
*1
しかし、この方法は正攻法で書いた場合の F# とは相性が悪いです。
そのため、 Persimmon.Dried では Arbitrary
にシュリンカとプリティプリンタも持たせ、 forAll
などには明示的に Arbitrary
を適用する方式にしています。
プリティプリンタ
このあたりは scalacheck に似せています。 そういう理由もあって型に対応する出力を自分でカスタマイズ可能です。
型クラス
とはいえ、型クラスを全く使わないわけにもいきません。 なぜかというと、 Prop 型への変換は基本的に単純作業になりやすいからです。
そのため、 Prop を返す関数や演算子では基本的に PropApply
という値を第1引数にとるに引数メソッド Instance
を持つ第2引数の型に限って、inline 展開と型推論によって型を変換します。
現時点では
- bool
- PropResult
- Prop
- Persimmon.TestResult
- Persimmon.AssertionResult
が変換対象です。
乱数生成
FsCheck は自前の、 scalacheck は標準のランダムモジュールを使っています。
Persimmon.Dried ではどうしようかなと迷ったのですが、この際なので FsRandom を使ってみることにしました。 既存かつ精度も申し分ないので問題ないと思っています。
実は単独でも動く
scalacheck ベースなので、当たり前といえば当たり前の話です。 ただ、 ConsoleReporter とコマンドラインパーサを(意図的に)実装していないので、スクリプトで実行しても何も出力されません。
この辺りも実装するかどうかは気分次第ですね。
おわりに
また一つ、オレオレライブラリが増えてしまった。
*1:Arbitrary 型がなくてもよいのでは、といわれることがあるのはこのためだと思っています
2014年簡易振り返り
寝落ちしていたらこの時間になっていたので、簡易版で。
生活
- 25歳定年説の歳だったけど新卒2年目(だった年)に定年という悲しみのネタになりそうだったので記事はかかなかった
- 定時に帰ると時間が空く
- 朝の時間帯にパf-マンスがでない
- 東京に行く回数がふえてしまった(移動時間もったいない疑惑)
- 俺タワーというゲームの影響(?)で工具にも興味がでてきた
- 年末年始に自炊することを…強いられているんだ!
- 「これは!」とおもったゲームをブクマするだけでなく買うべき
あとは目立った変化なし。 いいのかこれで…
言語など
- F#: 趣味と仕事あわせて一番書いた
- Scala: 趣味でいろいろ触った
- C#: 仕事で
- PowerShell: 少し書いた(が、まだ全然なので継続)
- 英語: 8月と年末にちょっと頑張った
来年は Erlang で何かライブラリを書いてみたいところ。 手を出すだけなら PureScript か Rust ?
年明けは、 Persimmon 用の VS TestExplorer 拡張と QuickCheck 系ライブラリ作りあげたい。
そして英語はどうしたものかな…。 それと某コンピュテーション式の本も再開しないと。
イベントとか
- Functional 忍者で何回かしゃべった
- Scalaz 勉強会でしゃべった
- 函数型なんたらの集い 2014 Tokyoを開催した
記事かけてない…関係者の皆様、ありがとうございました。
もうちょっと他のイベントでも発表できればよかったかな。
締め
毎年移動してばかりだ。
スコープやオフサイドルール小話
この記事は F# Advent Calendar 2014 の28日目の記事です。 引き続きガス欠気味なので小ネタで。
module Hoge = let hoge = // lazy もオフサイドルールの対象である lazy let a = 1 a + 1 do // スコープが異なるので定義できる let hoge = 1 let mutable x = 0 x <- printfn "test" 1 // これは無理 // x <- printfn "test" // 1 // 変数 x は上記 do 束縛のスコープ内のみ有効なので、以下のコードはコンパイルエラー // let fuga = x
気になったので F# Specification 3.0 を検索したら 8.13.2 にのみ "do bindings" という記述が見つかった。
"let bindings"も同じセクションでしか見つからなかったので、仕様書では bindings という単語をつかわない模様。
モジュール内での do
に関しては 10.2.5 で "do statements" と記載されている。
1ファイル内に複数のnamespaceを書く
この記事は F# Advent Calendar 2014 の28日目の記事です。 ガス欠気味なので超小ネタで。
namespace Hoge type Fuga() = class end namespace Foo type Bar() = class end
このコードは1ファイル内でコンパイルできます。
=
が必要ないのがミソですね。
入れ子の名前空間にしたい場合は、完全修飾で
namespace Hoge type Fuga() = class end namespace Hoge.Foo type Bar() = class end
と書けばよいです。
F# 製ユニットテスティングフレームワーク Persimmon について #FsAdvent
この記事は F# Advent Calendar 2014 の27日目の記事です。
内容は今年の10月末あたりから作っている Persimmon というフレームワークについてです。
諸注意
本記事に書くことはあくまでpocketberserker個人の見解です。 他の開発者がどう考えているのかは
ソースコードやドキュメントなど
persimmon-projects/Persimmon · GitHub
Getting Started があるので使い方はそっちを参考にしてください。
あと public に書き込める場所がほしかったので Gitter に部屋を作りました。
persimmon-projects/ja - Gitter
柿は何であって何でないのか
F# の、 F# による、 F# のためのテスティングフレームワークです。
コンピュテーション式を前提にしている時点で他の言語で使うことは想定していません。 そして、少なくとも私は極力複数言語をプロダクションコードに採用したくない(メンテナンス面倒)ので、ほぼ F# オンリーでの開発になるでしょう。*1 そういうわけで上記の見解です。
F# でユニットテストというと NUnit (とその Wrapper の FsUnit)が多数派です。 ビルドツールの FAKE にはこのほかに xUnit.NET、 MSpec のヘルパー関数が用意されています。 FsCheck も結構使われていますが、あれは性質が異なるので今回の話では対象外です。
さて、この3つのテスティングフレームワークには共通点があります。 それは C# で実装されている点と、既存テスティングフレームワークの手法を踏襲して実装されている点です。
前者はともかく後者については、
- 期待値と実行結果の型が異なっても実行時エラーでしか気づけないことがある
- 属性ベースのテスト識別
という問題があります。 属性に関しては単に面倒くさいのと、渡せる型に制約があって一部F# の型は使えない点ですかね。
特徴と類似品
特徴はドキュメントに書いてあります。 まぁ、それだけでおわらせるのもアレなのでちょっとだけ追加。
副作用
Persimmon では、変数、unit
を引数にとる関数、static member なプロパティがテスト実行の対象になります。
変数束縛で済ませるか関数にするかは、そのテストに副作用が含まれるかどうかで判断するのが良いのではと思っています。
unit
をみたら副作用があるかもしれないと思え、ということですね。
型安全
NUnit の Assertion は型安全ではなかったりしますが、Persimmon の Assertion はよほど変なことをしない限りは型安全です。
コンピュテーション式
「属性を使わないなら、型で表現すればいいじゃない」
とはいえ、ただ型で表現するだけだと色々と面倒なこともあるので、Persimmon ではコンピュテーション式で記述できるようになっているとか、いないとか。
現時点で、master ブランチに4つのコンピュテーション式が存在します。
- テストの記述
- parameterized test の記述
- 例外発生を期待する
- Asyncな値を実行し非同期例外を補足する
テスト、アサーションの合成
Persimmon のテストやアサーションは値を返します。
そして、 unit
を返すことは他のテストやアサーションに影響を与えないことを宣言することになります。
逆に、テストやアサーションの返り値を使って別のテストやアサーションを行うということは、それらが別のテスト、アサーションの通過/違反に依存していることを明示します。
テストを合成できることで
- 状態を変更するテストと状態を変更しないテストの分離
- テストの重複除去
を意識できるのではないか、と考えています。 あとはテストを解析することでテストの依存グラフを出力できるなるはずなので、何かしら役にたたないかなと。 テストを合成できるようにして依存グラフ云々みたいな話は過去に id:kyon-mm さんとも話していた気がするけど、参照実装や資料があるのかはちょっとわからないです…。
Monad ではない(はず)
何らかの制約をかければあるいは…ですが、結合則を証明できる気がしない。
AssertJ
最近になって存在を知った AssertJ というライブラリがあります。 Persimmon 実装中に kyon-mm さんに存在を教えてもらったのでこのライブラリの影響を受けた、というわけではないのですが、やりたいことの一部が似ています。
"継続可能なアサーション" は AssertJ では "Soft Assert"と呼ばれているようです。 この名前を踏襲するかは未定ですが、少なくとも新しい概念ではないのは確かなので、そのうちドキュメントに反映したいところです。
AssertJ の SoftAssertion との違いは
- AssertJは任意、Persimmonは強制
- AssertJの実装はstateful、Persimmonの実装はstateless
- AssertJのAssertionは
void
だが、PersimmonのAssertionは値を返すことができる
SoftAssertionsが stateful なのはSoftAssertionのAPIドキュメントに書いてあります。
また、 assertAll
メソッドを呼び出さないとテストに通るだろうこともこのドキュメントに書いてあります。
これはこれでありだと思いますが、合成できるなら合成したほうが楽かもね、とも思います。
NUnit, xUnit.NET, MSTest
主な違いは
- 型安全性
- 属性ベースと型ベースの違い
- 例外によるハンドリングか値を返すか
です。
.NETの枠組みで既存の代替物となることはないでしょう。 テストとプロダクションコードが別言語でも良いと思っている人は移行する可能性はありますが、C# Wrapper を提供しない時点で C#er の方々が使う確率は減ります。 F#書きづらい(or 嫌い)という人もいるでしょうし。 あくまで、F#でのテストの代替物の可能性があるといいね、というレベルです。
モデル
1.0.0 αリリース以前にモデルが一度変更されています。 どれくらいかわったかというと これくらい。
実装前の検討段階の資料はプロジェクトのWikiにありますが、最新版は存在しません。 というわけで、最新版の日本語情報を作ってみました。 ある程度固まったら英語に書き直してから日本語に再翻訳して公式ドキュメントに反映したいと思います。
エラーが AssetionResult<'T>
ではなくTestResult<'T>
にあるのは、例外発生時は多くの場合テストの続行自体不可能である場合が多いためです。
Visual Studio Test Explorer 対応
1.0.0 をリリースしたら作業を再開する予定です。 リフレクション使って実装しないといけないらしいので既にやる気が減衰していますが、まぁないと不便だし一回作れば楽になるのはわかっているので…冬休みの宿題ですね。
問題点
現時点で問題かなと思っていることをさらっとかいてみます。
assertion が貧弱
現状の Assertion は等値比較と述語の真偽比較くらいしか関数がないので貧弱です。 とはいえ、assertHoge 関数を増やしても覚えるのが面倒ですし、言葉つなぎ系は面倒なのであまり実装したくないなという話もあって今の状況なのですが…。
ぱっと思いつく解決策の一つは Power Assert ですが、この issue を見てもらうとわかる通りPower Assert ではない別の Assert を模索しようという方針になっています。
実践的なサンプルが少ない
これはそのうちアプリケーション作ってみないとなー、とかそういう話ですね。 今の FizzBuzz とポーカーの例だけでは情報量が少ない…。
一部既存ライブラリとの相性が悪い
NUnit や xUnit.NET と連携することを想定しているライブラリは、その性質上 例外を用いた Assertion の通知を行うものが多いです。 そしてこの方法は、Persimmon との相性はあまりよくありません。
こればかりはどうしようもないので、「ないなら作るしかねぇ」的な方針にせざるを得ないですね。 ぶっちゃけ FsCheck がそんな感じで相性が良いとは言えないので、 Persimmon 用に作ることを画策しています。
開発体制
今のところ3名で開発が行われています。
- Gab-km さん
- 原案発案
- 怒涛のドキュメントレビュー(超重要)
- 某ライブラリの開発があるため、現状Persimmonのコードを変更することは稀?
- bleis-tift さん
- 改良案発案
- 新モデルの構築者
- 忙しくて触れないことがあるようだ
- pocketberserker
- その他
- 上記2名が動けない時に動く役
今のところバランスは保てていますが、今後開発速度をあげたいなら人手がほしいところ。
プロジェクト名の由来
かいつまんで書くと
- 某社内チャットでプロジェクト名を募集
- 「秋だから柿か栗では?」
- とりあえず柿と栗の英単語を調べよう
- Persimmon、よさそう
- 「秋にちなんだ名前ってことは秋中に動くようにするんだよね?」「えっ?」
なぜ FAKE を使わないのか
趣味の問題です。
おわりに
1月までには TestExplorer 拡張を含めてリリースしたいところ…。
FsCheckのこまごまとしたこと #FsAdvent
全国のF#erの皆様こんにちは、もみあげことなかやんぺんぎんです。
ここからは F# Advent Calendar 2014の延長戦(25日目)です。 英語圏と張り合ってるわけじゃ、ないんだからね!?
今回は今年めでたく正式版がリリースされた FsCheck についてちょっとした変更点などの紹介です。
利用実績
名古屋某社のいくつかのプロジェクトで利用実績があります。 OSS での利用例は…たくさんあるので割愛します。
FsCheck の target .NET Framework verison
FsCheck は Release Note 的には 1.0.0 より(実際は 0.9.3 あたりからだった気がする)、.NET Framework 4.5 以降のみをサポートするようになりました。
これは、 コード内で使われている ExceptionDispatchInfo
が 4.5 以降にしかない機能だからです。
まぁ、テストプロジェクトのバージョンが指定されることはほとんどないので、レガシー環境でない限りはそんなに気にならないかと。
string generator の挙動
0.9.3 までは、Default で提供されている string generator は null を入力として含みませんでしたが、 0.9.4 からは null も生成対象になりました。
これはこれで正しいと思う一方で、面倒くさい null を生成しない generator がほしいという意見もちらほら見かけました。
そこで、 1.0.4 からは NonNull 型を導入することで、null を含まない ランダムな値の生成が可能になりました。 たとえば string なら
check <| fun (s: NonNull<string>) -> let s = s.Get // 値の取得 ...
こんな感じにすればよいです。 いちいち束縛するのが面倒な場合はアクティブパターンでいい感じにするとはかどるかもしれません。
let (|NonNullStr|) (s: NonNull<string>) = s.Get ... // s は string check <| fun (NonNullStr s) -> ...
IRunner と 拡張ライブラリ
他テスティングフレームワークと連携する方法としては、0.9系までは IRunner
を実装する一択だったわけですが、 1.0.0 以降は FsCheck.NUnit や FsCheck.Xunit という拡張ライブラリが登場したため、簡単に使いたいならこれらのライブラリを使うほうが楽です。
とはいえ、出力の制御をおこないたい場合などはやっぱりIRunner
を実装する必要があるのでお役御免というわけではないです。
FsCheck.NUnit と FsCheck.Xunit
NUnit と Xunit は公式から拡張ライブラリが公開されています。
このライブラリを入れることでどのように記述がかわるのかは…具体例を見てみましょう。
https://github.com/pocketberserker/FsAttoparsec/commit/5b8861f95ebdadf8a57b4c8cb8402788b8be9f59
FsCheckAddin
型の部分が FsCheck.NUnit をインストールした際についてくるコードです*1。
上記コードで目立つ変更点としては、 NUnit の TestAttribute
から FsCheck.NUnit の PropertyAttribute
に変わっているところでしょうか。
PropertyAttribute
を使うことで、テストごとの設定を変更できたり、ラムダ式を使わずにテストできます。
変更できる値は
- MaxTest
- MaxFail
- StartSize
- EndSize
- Verbose
- QuietOnSuccess
- Arbitrary
です。各設定の意味については公式ドキュメントを読んでください。
FsCheck.Xunit のみの機能として、 ArbitraryAttribute
が存在します。
こいつはクラスやモジュールをターゲットに、 Arbitrary を指定できるものです。
NUnit 拡張にはこの機能はないので、いちいちメソッドに Arbitrary を書くか、 Arb.register
するしかありません(後者はグローバル汚染しちゃうけど…)。
2014/12/24 時点での NUnit の注意事項
完全に余談ですが、2014/12/17 に NUnit 2.6.4 がリリースされました。
が、対応する NUnit Test Adapter(Visual Studio拡張)がまだなく、かつ FsCheck.NUnit との相性が悪いのか、VS Test Explorer 上でテストが実行できません。
でも NUnit.Runners 2.6.4 に同梱されている nunit-console なら実行できるだろうと慢心していましたが、こちらはこちらで "nunit-core-interface 2.6.3 をよこせ"と言って落ちる…。
というわけで、いろいろ環境が追いつくまでは大人しく 2.6.3 を使いましょう…。
CSharp Wrapper
あるらしいですが、使ったことがないので使用感がわからず…そのうち試してみたいところです。
MSTest 用の拡張もあるらしいです。
ツールや環境整備
1.0.0 リリースのタイミングで整備されました。 具体的には
- FAKE を使って自動リリース
- FSharo.Formatting によるドキュメント作成
- non Windows 環境での mono でのビルド
- CIサービス(AppVeyor, Travis)との連携
です。 最近の オープンソース F# プロジェクトはだいたい正式リリースまでの間にこの辺りを用意するみたいですね。*2
その他
README の一説に以下の記述があります。
Since v0.5, scalacheck has influenced FsCheck as well. Scalacheck is itself a port of QuickCheck to Scala.
FsCheck はだいたい QuickCheck と scalacheck の影響を受けている、と。 出現時期がわかる一文ですね。
おわりに
良い property based test ライフを!
FsAttoparsecについて
この記事は F# Advent Calendar 2014の16日目の記事兼ML Advent Calendar 2014の16日目の記事になります。
今日はF# のネタリストの中から、FsAttoparsecについてです。
attoparsec とは
attoparsec は Haskell のパーサコンビネータライブラリの一つです。 CPSとインライン展開を用いることで高速化を図っていることが特徴です。 あと実装がそこそこ小さいです。
速度を重視する代わりに犠牲となっているものは、エラー出力です。 まぁ、仕方ないですよね。
なお、attoparsec の現在の実装はこの記事 曰く第3世代だそうです。
FsAttoparsec とは
F# 移植版(+ちょっとした機能追加) attoparsec です。 本家とは違い、今のところ高速とは名乗っていません(実際遅い)。
実装理由
F# のパーサコンビネータライブラリ事情 - pocketberserkerの爆走
この記事にも書いたのですが、FParsec は速いけど文字列のみ、ParsecClone はバイナリに主眼をおいているけどシグネチャがよみづらいし実装間違えると例外が投げられるのがつらいという現実がありました。
というわけなので、上記2つ以外にも候補があってもいいよねと思い、ちょうど現実逃避もしたかったし CPS クンカクンカしたかったので実装しました。
特徴や欠点
- pure F# implementation
- 小さな実装
- トークンパーサ
- パフォーマンスへの影響
- 開発者が日本人
pure F# implementation
F# オンリーで FParsec に追いつきたいという意地。
小さな実装
全実装行数がFParsecの1ファイル分だったりするという…FParsecが巨大なだけですが。
トークンパーサ
本家 attoparsec には存在しないのですが、FsAttoparsec には Parsec の TokenParser も実装されています。
パフォーマンスへの影響
F# で GHC 拡張の RankNTypes をエミュレーションするにはメソッドベースで CPS にしなければならず、メソッドを実装するためにはクラスベースにしなければなりません。 このためインターフェースを利用せざるを得なくなり、仮想テーブルの影響でパフォーマンスに影響が出るのが一つ目の問題です。
メソッドはビルド環境設定によっては最適化されないので、スタックオーバーフローを起こします それを回避するために trampoline を使っているのですが…当然のごとくパフォーマンスに影響します。
開発者が日本人
母国語で実装についてきけるのは楽なのではないでしょーか!
むしろ英語で訊かれても答えられない可能性…。
利用実績
F#erが多く在籍している名古屋の会社のどこかで使われているらしいです。
サンプル
テストコード内にLTSVパーサの例が、ベンチマークプロジェクト内にJsonパーサの例があります。
あとは Haskell のコードを参考にするとか、ですかね。
実際どのパーサコンビネータライブラリがいいの?
ここで、とある方の意見を見てみましょう。
これは、文字列解析に関して言えば正しいと思います。 利用実績も多いでしょうし、速度も申し分ないので。
他のデータ構造に関してはどうですかね… ParsecClone も FsAttoparsec も一長一短あるので何とも言えません。
参考資料など
attoparsec の知識がそれなりにそのまま使えます。
このあたりは参考になるかもしれません。
移植にどのくらいかかるのか
FsAttoparsecの移植はコードリーディングからバグ修正まで含めて2週間くらいかかりました。
知っておくべき知識は(速度を求めないなら)
と、多くもなく難しいわけでもないので、わりとどうにかなるのではないでしょうか。 速度を求めなければ。
OCaml は確かモジュールを使えば rankNtype はなんとかできたはずですし、わりと問題なく実装できそうな気がしますね。