kzrnm / ac-library-csharp

42 stars 5 forks source link

[WIP] make test #51

Closed key-moon closed 2 years ago

key-moon commented 4 years ago

NUnit でテストの雛形を書いてみました。現状ボイラープレートが凄いですが、いずれ TestAttribute に取り込めたら良いなと思い、敢えてほぼ同じ形での実装にしました。(Validator 周辺のまどろっこしい部分がそうです。) 後々、例えば以下のような実装にできたら良いなと思っています。

[ProblemTestCase(@"https://judge.yosupo.jp/problem/scc", typeof(SCCValidator))]
public void Practice(TextReader reader, TextWriter writer) { /*略*/ }

class SCCValidator : IValidator
{
    public bool IsValid(string input, string answer, string output) { /*略*/ }
}

テストの内容は現状本家の example のみです。(他は Validator も要らず、普通に書けば良いです) IValidator には

を入力しています。(testlibyosupo judgechecker と同等の挙動をしています。) 独自 checker が必要なものは現状 AlwaysFailValidator を適用しています。

テストのダウンロードについては、最初は OJ の実装(AOJ/yosupo judge)を素に独自で実装しようかと思っていましたが、特にyosupo judgeはローカルにクローンした後ファイルをいじらないとだめな部分が多いため OJ をSubprocess で叩いてしまうのが良いかなと思っています。どうでしょうか?

kzrnm commented 4 years ago

外部からテスト引数を取ってきてパースしてというのは単体テストの範囲を逸脱するので初期スコープにするのは避けたほうが良いかと思います。

テストはベタ書きの方が個人的にはしっくりきます。

ベタ書きの例を作ったので、このブランチに対してPull Requestを出してみました。 #52

takytank commented 4 years ago

一般的な話として、既に正しいということが分かっている入力と出力が存在している場合、自分でテストケースを考えるよりも先にまずそれを使うべきではないでしょうか? 今回は特にALCのために用意された問題セットで、初期テストケースとしては最適だと思います。(サンプル以外のテストケースも使えればなおいいんですけど。)

そして、外部にあるテストケースを使うにあたっては、それを写してべた書きするよりは、プログラムに任せて入力する手法の方がミスも手間も無くてよいのかなと思います。

key-moon commented 4 years ago

単体テストの実装は基本的に AtCoder の実装に従いたいと思っています。(Fenwick Treeであればこれで、ランダムケースを用いた愚直ケースとの比較や(このファイルではしていませんが)TLEチェックなどをしています。)

今回書いたものは AtCoder 側では「そのまま提出すればACが貰える使用例コード」という位置づけであろう Example ディレクトリにあるものの移植なので、実際ならばテストとして回す必要すらありません。(AtCoder側では現に回っていません。) ただ、このような「提出すればACが貰える」というコード群をテストとして用いることは競技プログラミングのライブラリでは最も手軽に用いられている手段で、そのようなテストのサポートをする Verification Helper というフレームワークも存在します。 今回はそれと同じような働きをすることを期待して書きました。元の Verification Helpercsx のファイルでのライブラリをテストすることができ、自分のものは右のように書いています。(Primes.test.csx)

kzrnm commented 4 years ago

今回は特にALCのために用意された問題セットで、初期テストケースとしては最適だと思います。(サンプル以外のテストケースも使えればなおいいんですけど。)

この場合の入出力は網羅性が欠落しています。コードのカバレッジについてもそうですし、あくまで簡単な入口にはなりますが、テストとしては不十分です。

そして、外部にあるテストケースを使うにあたっては、それを写してべた書きするよりは、プログラムに任せて入力する手法の方がミスも手間も無くてよいのかなと思います。

これについては、明確に反対です。「プログラムに任せて入力する」のは入出力を含めた結合テストになり、テスト対象が膨らみます。

AtCoder の実装に従いたいと思っています

賛成です。一般的なテストに則るべきかと思います。

Example ディレクトリにあるものの移植なので、実際ならばテストとして回す必要すらありません。(AtCoder側では現に回っていません。)

公式の方でテストが出ていることに気付きませんでした。ありがたいです。

テストとして回す必要すらないという認識のとおり、C#版でも Example はテスト本体に入れる必要はないかと思います。

kzrnm commented 4 years ago

試しに FenwickTree, Segtree, LazySegtree のテストを移植してみます。

terry-u16 commented 4 years ago

ベタ書きだと管理がちょっと大変そうかな……という気がしています。 テストケースを落としてくるプログラムにバグが混入する可能性があるのと同様に、ベタ書きもヒューマンエラーが生じる可能性があるので、一概にベタ書きが優れているとも言えないのではないでしょうか。

今から自前で作るのはおっしゃる通りそれ自体のテスト等も必要で大変ですが、それなりに使用実績があるOJを直接叩くのであればさほど問題はないのかなと思います。

takytank commented 4 years ago

公式のテストがあるのは自分も知りませんでした。 確かにそれを移植してくるのが一番良さそうです。

key-moon commented 4 years ago

データを直書きするのであればサンプルケース程度の小さなものしかコードに含められないと思うので、それをするくらいならばテスト自体が無くても良いなと思いました。

「プログラムに任せて入力する」のは入出力を含めた結合テストになり、テスト対象が膨らみます。

「入出力パートが絡んでいる上、曲がりなりにも『問題』として成立しているものを使用するので『単体テスト』としてのスコープとしては広すぎるため不適切である」という指摘と、「自分でテストケースを fetch するようなテストの拡張を作成するのは fetch する部分までまとめてテストすることになって不適切」という指摘のどちらかが少し分からなかったです。

前者については、ユースケースである競技プログラミングのコードでのテストなので結合テストとも捉えられますが、競技プログラミングにおいてのコードは厳密に仕様が定まっており誤答判定が可能であるために単体テストフレームワークを使うことは問題にならないと考えます。

後者については、モック問題として用意されている a+bMany a+b を用いてテスト自体のテストをすればよいと思うため、問題にならないと思います。が、外部に依存関係があり、それの仕様変更に対して脆弱なので不適切と考えると納得がいきます。個人のライブラリであればそれを許容することで競プロライクなテストを実現してしていますが、AtCoder における実装でこの形式のテストを採用していないことからもこの手法は共有のライブラリ等の正確なテストが求められるものには不向きであるなと思います。

纏めると、私は「外部に依存関係を持っているのが不健全である」という部分が問題であると考えます。そうなるとテストケースを生成した上でそれもコミットしてしまうと解決すると思いますが、AtCoder 側でそのテスト手法を取らずに単体テストを作成している以上、現状そのようなテストは不要なのかなと考えます。

kzrnm commented 4 years ago

https://github.com/key-moon/ac-library-cs/issues/19#issuecomment-693514475 にコメントしました