世界を動かす技術を、日本語で。

仮説、反仮説、統合

概要

  • Hypothesis の開発者が Antithesis に参加し、 Hegel という新しいプロパティベーステストライブラリを発表
  • Hegel は多言語対応を目指し、まず Rust 用をリリース、今後 Go、C++、OCaml、TypeScript にも展開予定
  • プロパティベーステスト の利点と Hegel の特徴を具体例とともに解説
  • Hypothesis の強みと、それを各言語で活用するための Hegel の設計思想
  • HegelAntithesis の連携によるバグ発見力の向上を目指す

Hegel:新しいプロパティベーステストライブラリの誕生

  • Hypothesis の開発者が Antithesis に参加し、 Hegel を開発
  • Hegel は、 Hypothesis の高品質なプロパティベーステストを多言語に展開するプロジェクト
  • Rust 用Hegelを最初に公開、今後 Go、C++、OCaml、TypeScript 向けにも順次リリース予定
  • Antithesis との連携で、バグ検出能力をさらに強化

プロパティベーステストとは何か

  • テストケースを手動で作成する代わりに、 値の範囲 を指定して自動生成
  • 例:パーサーがクラッシュしないこと、データ変換のラウンドトリップで値が一致すること
  • Hegel では、独自のジェネレータを組み合わせて複雑な値も生成可能
  • 一般的なバグの発見例:ゼロの扱い忘れ、呪われたデータ型、構造的不変条件のミス
  • モデルベーステスト も容易で、実装とモデルの一致を検証可能

Hypothesisの特徴とその強み

  • Python で最も普及しているプロパティベーステストライブラリ
  • 高品質な ジェネレータ 群と柔軟な拡張性
  • 内部シュリンキング により、わかりやすく再現性のある失敗例を自動抽出
  • テストデータベース により、失敗したテストを再実行時に即座に再現
  • QuickCheck 系ライブラリとの差別化:ユーザビリティ重視、実用的な拡張

Hegelの設計思想と他言語対応

  • 全機能を Hypothesis 本体で実装し、各言語には薄いクライアントを提供
  • 各言語でネイティブに感じられるAPI設計に注力
  • 新しい言語向けのHegelライブラリは、 Hegelプロトコル 実装とAPI設計のみで展開可能
  • メンテナンス負荷 を抑えつつ、各言語で高品質なプロパティベーステストを実現

HegelとAntithesisの連携

  • Hegel を使ったテストを Antithesis 上で実行し、さらに強力なバグ検出・デバッグ体験を提供
  • Hegelテストはローカル環境でもAntithesis環境でも同様に動作
  • 現状、Hegelは高度な分散システムのテストには未対応だが、今後の改善を予定

Hegelを使うべき理由

  • AI時代 のソフトウェア開発において、プロパティベーステストの重要性が増大
  • Hegel は高品質なテスト自動化により、バグを早期発見・修正
  • 多言語・多環境で一貫したテスト品質を維持可能
  • 今後のソフトウェア開発スタイルの中心的存在となる可能性

まとめ

  • Hegel は、 Hypothesis の技術とノウハウを活かし、あらゆるプログラミング言語で高品質なプロパティベーステストを実現する新しいライブラリ
  • Antithesis との連携で、さらに強力なバグ検出・再現性を提供
  • 今後のリリースや機能拡張に注目

Hackerたちの意見

投稿者です!質問があればどうぞ。ヘーゲルについてでも、プロパティベースのテストについてでも、「なんでPythonライブラリのRustバインディングを書いたの?」みたいなことでもOKです!

正直、そのセクションの最初の数語を読んだとき、「だからClaudeを使ってHypothesisをRustで書き直したんだ…」って続くと思ってたから、かなり驚きました!

投稿の中で、Hegel/HypothesisとQuickCheckの間にデザインの違いがあることに言及していますが、それはPythonや非HaskellプログラマーとHaskellプログラマーの態度の違いによるものです。Haskellの世界から来た者として(Haskellが完璧な言語だとは思っていませんが)、これらの違いについて詳しく教えてもらえますか?

既存のRustのProptestライブラリの代わりに、これを使う理由は何?

これは質問じゃないけど、Hypothesisに感謝したくて。私は定期的に使ってるんだ。数年前、半形式的に検証されたファンドとアカウント管理サービスを構築しなきゃいけなくて、その正しさを確認するためにHypothesisの状態ベースのテストを使った。こんな小さなフレームワークがどれだけ貴重だったか、言葉では表せないよ。その後、製薬関連の人と話したとき、彼はAntithesisを使って自分の製品を検証しようとしてたんだ。その時、Antithesis(会社)は彼に合わないって言ってた。私は前のアプローチに似た提案をしたけど(Antithesisは含まれてなかった)、彼らが最終的にどうしたのかは全然知らない。でも、HypothesisとAntithesisがついに手を組んだのを見るのは嬉しいね。

地球上でコードを投稿する方がブログ記事よりも良いコミュニティかもしれないので、TL;DRとしてこれはユニバーサルなプロパティベースのテストプロトコル(https://github.com/hegeldev/hegel-core)とライブラリ群(https://github.com/hegeldev/hegel-rust、続きは後で)です。PBTの世界で多くの人と話してきたけど、こういうのがPBTエコシステムの最終目標だと考えている人が多かったです。いつかはこうなるだろうと思っていたので、誰かがやる必要がありました。実際にやってみることができて、素晴らしいPBTをあらゆる言語に持っていけることにワクワクしています。AIコードのおかげで、どの言語でも素晴らしいPBTが急に重要になってきたのも良いタイミングです!

プロパティベースのテストは、AIエージェントベースのソフトウェア開発がひどいことにならないための大きな部分になるでしょう。間違いないと思います。テストは重要であり、AIの利用が増えることでさらに重要になる可能性があります。だから、より良いテストが役立つし、PBTもその一部です。ただ、問題はテストが本当に意図したことをテストしているかを確認することです。ミューテーションテストは、エージェントが人間の介入なしで良いカバレッジを得るのを可能にし、PBTはテストをより良く、読みやすくします。でも、やっぱり人間がそれを読んで理解しなきゃいけないし、1日に何千行も生成していると主張する人たちの中には、理解していない人も多いと思います。たとえテストが素晴らしくて、人々が慎重にレビューしても、それだけではひどいことが起こらない保証にはなりません。AnthropicのCコンパイラの実験は、悪いテストのせいで失敗したわけではありません。テストは良かったし、手作業でテストを書くのに人間が何年もかかりましたが、エージェントはそれでも収束しませんでした。良いテストはAIがひどいソフトウェアを生成しないための必要条件だと思いますが、まだ十分条件には達していないのは明らかです。「大きな部分」とは言えるかもしれませんが、他にも大きな部分がまだ欠けています。

実は、PBTが役立つ別の視点があると思うんだけど、それはブログ記事では探求されていなかったんだ。その視点は可読性です。AIが書いた雑なソフトウェアが正しいことをしているかどうか、どうやって確認するの?普通はコードを全部読むよね。残念ながら、それはAIを使わないのとあまり変わらないくらい手間がかかる。でも、包括的なプロパティベースのテストがあれば、ソフトウェアが正しいことをしているかを確認するために、プロパティベースのテストだけを読むことができる。たとえて言うなら、機械でチェックされた証明を見る必要はなくて、主張が正しいことを確認するだけでいい。

でも、問題はテストが本当に意図したことをテストしているかを確認することです。確かに。PBTでこれを偽るのは、例に基づくテストよりもずっと難しいけど、それでも悪いプロパティベースのテストを書くことはできるし、エージェントはそれを得意としています。一般的に、プロパティベースのテストを持つエージェントは、例に基づくテストだけのエージェントよりも自分を騙さないことが多いと感じていますが、それでもClaudeに対して叫ぶ時間はたくさんあります。 > だから「大きな部分」とは言えるかもしれませんが、他にも大きな部分がまだ欠けています。ここには異論はありません。エージェントによるコーディングを解決するとは言っていません。私たちはただ、テストを行う人々をテストしているだけで、エージェントの世界では良いテストツールが特に重要だと思っています。

確かに、テストは重要であり、AIの利用が増えるにつれてさらに重要になると思う。だから、より良いテスト、PBTも含めて、役に立つよね。カリー・ハワード同型性を考えると、PBTを実行する代わりに、HWモデルの仮定のもとでAIにバイナリ実行可能ファイルの特性を直接証明させることはできないかな?PBTを否定するつもりは全くないけど、これならもっと早くて信頼性も高いかもしれないね。

人間が手作業でテストを書くのに何年もかかり、エージェントは収束に失敗した。エージェントが今日失敗していることを、未来でも引き続き失敗するだろうと仮定するのは危険だと思う。つまり、エージェントが1年か2年の間に始めた軌道で改善を続ける楽観的な見方をするなら、彼らのためにどんなツールやインフラが必要になるかを考える価値があるということ。今からその未来に向けて構築を始める企業は、2年後に新しい現実に目覚める人たちよりも、より良い立場にいるだろう。

DRMacIverさん、これがさまざまな言語の既存のプロパティベースのテストエコシステムにどのようにフィットするかについてコメントできますか?たとえば、Rustでproptestを使っている場合、なぜHegelに切り替えるべきなのでしょうか?

既存のエコシステムにどうフィットするかの短い答えは…競争かな。これらのライブラリに取り組んでいる人たちにはすごくリスペクトしてるけど、Hypothesisベースのアプローチが他の人たちが採用している様々なアプローチよりも良いと思ってる。最初に使う自然言語が、すでにかなり良いプロパティベースのテストライブラリがあるものだから、ちょっと気が引けるけど、結局それが正しい選択だったと思う。なぜなら、それらは人々が正しいソフトウェアを書くことに関心を持っている言語であり、私たち自身がツールを最も欲しい言語でもあるから!今のところ、もしプロパストを楽しく使っているなら、Hegelに切り替えるべきかどうかは明確じゃないかも。試してみた人の話を聞きたいけど、今の状態では心から「これが正しい選択だ」とは言えないな。最終的にはそうなると思うけど。Hegelのアプローチが明らかに良いと思う点は、以下の通り。もしグリーンフィールドから始めるなら、Hegelを試す価値があるかも。* プロパストよりもずっと良いジェネレーター言語(プロパストの選択は本当に好きじゃない。これは個人的な美的好みもあるけど、明示的に構築されたジェネレーターの方がアプローチとしてうまく機能すると思うし、Hypothesisでも証明されてる)。Hegelには、欲しいデータを生成するための柔軟なツールがたくさんある。* Hegelは、データの有効性要件を常に尊重した素晴らしい縮小を提供してくれる。もし何かが常に真であることを保証するジェネレーターを書いているなら、それは縮小されたデータにも当てはまるべき。これは…プロパストではせいぜい部分的にしか真実じゃない。オリジナルのクイックチェックとその純粋な型ベースの縮小ほどの足元をすくうような問題はないけど、良い結果を出す縮小と、有効なデータを確実に得られる縮小の間で選択を迫られることが多い。* Hegelのテスト再実行は、シード保存よりもずっと良い。失敗したテストを再実行すると、ほぼすぐに同じように失敗する。Hypothesisモデルを使わないアプローチでは、最善の結果はランダムシードを保存して、その失敗例から縮小を再実行することだけど、これはかなり遅い。多分他にも生活の質を向上させる改善点がたくさんあるけど、これらは私がプロパストを使ったときに際立っていた点で、Hypothesisモデルとより古典的なQuickCheck派生のものとの大きな対比でもある。

誰も使ってないけど、Deno用にプロパティテストライブラリを書いたよ。[1] これは「時々」アサーションの形を持っていて(Antithesisからインスパイア)、内部縮小(Hypothesisからインスパイア)を使ってる。でも、まだ「盲目的な」ファズァーで、コードカバレッジからフィードバックを得るようなものを書けたらいいなと思ってる。今は自分でコードカバレッジを実行して、テストデータ生成をどう改善するかを考えなきゃいけないんだ。[1] https://jsr.io/@skybrian/repeat-test

でも、これはまだ「盲目的な」ファズテストで、コードカバレッジからフィードバックを得るようなものを書けたらいいなと思ってる。これに関しては、単純な試みがあったりするけど、例えば100回テストをする代わりに、カバレッジが増えている限り続けるみたいな感じ。https://arxiv.org/pdf/2203.00652 のChoice Gradient Samplingアルゴリズムは、生成器をもっと繊細に操る方法としていい感じだと思う。その論文では、拒否サンプリングのときに廃棄を避けるために使われているけど、新しいカバレッジに基づいて「報酬」を与えるように再利用できる気がする。

こんにちは、デイビッド!リリースおめでとう!Hypothesisのビットストリームベースの縮小を試すのが楽しみだよ。prop_flat_mapは扱いにくいから、いくつかのプロパストベースのテストをHegelに置き換えたいな。先週Hegelを少し見てみたけど、タイプのための標準的なジェネレーターをどうやって作るかがはっきりしなかった(プロパストのArbitraryに似てる)。大きな構造を生成して、シリアライゼーションのラウンドトリップをテストするのにとても役立つんだ。特に、テスト戦略ライブラリには、ビジネスロジックタイプに非常にうまく機能するderiveマクロがあって、例えば10-15の列挙型バリアントがそれぞれ0-10のサブフィールドを持つ場合がある。これが今日サポートされているのか、将来的にこのような構成をサポートする計画があるのか、興味があるな。編集:ああ、DefaultGeneratorを導出するマクロを完全に見逃してた!うっかり。

そうだね、#[derive(DefaultGenerator)]generators::default()がここでは正しいツールだよ。ここは私たちがあまり実際に使っていない分野だから、何か問題があればフィードバックをもらえると嬉しい!from_typeはHypothesisの最も強力で使いやすい戦略の一つだと思うし、Rustではそのレベルには達しないかもしれないけど、それでもかなり素晴らしいものが得られると思う。

ここにはHegelのジョークのためだけに来た。

他の言語からPythonを使うのは最悪だね。この手のテストは大好きだけど、この実装は私には合わない。Pythonに依存してるって知ったときはすごくがっかりしたよ。

うん、cargo testuvみたいな別のバイナリを必要とするのはイディオムじゃないよね。99%の確率で、Rustプロジェクトに近づいてcargo testを実行すれば、そのまま動くはずなんだ。

ヘーゲルの引用が好きだな。Hypothesisが素晴らしいのは知ってるから、これもきっとそうだろう。投稿に対する本当の不満ではないけど、PLオタクな私にとっては、Ruatのコードが意味のある形で解析するのが一番難しいと思う。プロパティ定義がどう設定されているかを理解するために、ただ「かっこいい」ってそのまま受け取った感じだし…やっぱり、最近の言語に比べてRustにあまり興味がないのが原因かな。まあ、スキルの問題だから不満ではないよ。あなたの成功を願ってます!

プロパティベースのテストはいいけど、カバレッジ駆動にするのはゲームチェンジャーだね。ナイーブなランダム入力では千年経ってもトリガーされないコードパスを探索してくれる。Rustでは、libFuzzerとArbitraryクレートを使って生成器を導出するのがすごくうまくいくよ。

[免責事項: Antithesisを始めました] HegelのテストをAntithesisで実行すると、これが無料でもらえるよ(さまざまな「非ローカル」アサーションや、並行または分散コードでも完璧な再現性なども含めて)。でも、Antithesisの外で基本的なカバレッジガイダンスをハックするのは難しくないよ。それは大きなクラスのプログラムにはうまく機能するけど、大多数には合わないね。