17

私はErlangで少し時間を過ごしており、書いているコードにTDDを適用したいと思っています。

標準ライブラリのEUnitは、通常のスタイルのコードをテストするための優れた従来の単体テストフレームワークを提供しますが、ErlangでLOTで使用される並行コードのテストに特に役立つものはないようです。

ここでErlangについて話していることに注意してください。これは、並行プロセス間の通信に(共有状態ではなく)メッセージパッシングを使用するため、共有状態言語で並行コードを単体テストする手法は適用できない場合があります。

Erlangで並行コードをテストする良い方法を見つけた人はいますか?

4

6 に答える 6

9

Concuerrorと呼ばれる、同時実行 Erlang アプリケーションをテストするための、新しく (2011 年に) 開発されたクールなソフトウェアを見つけました。それに関するいくつかの 論文とgithub のリポジトリがあります。どうやら、独自のスケジューラを使用し、プロセス間のさまざまなインターリーブを体系的にテストすることで機能しているようです。

また、Dialyzer (DIScrepancy AnaLYZer for ERlang) (論文チュートリアルマニュアル) も言及する価値があります。これは、エラーを見つけるためのコードの静的分析ツールです。これは、一部の同時実行エラーの検出もサポートしています (論文を参照)。

ダイアライザーは比較的成熟したソフトウェアのようですが、私はこれらを自分でテストしていません。どちらのプログラムにも、テストを操作するための GUI があります。

PS。非並行部分については、EUnit と QuickCheck (無料版もあります) が正常に動作するはずです。

于 2011-10-10T13:38:59.190 に答える
5

質問は少しあいまいですが (「Erlang は並行処理です。Erlang でテストしてください!」)、少し詳しく説明します。

Erlang コードのテストは、単純なもの (正しい入力が正しい出力を生成する) から、コンポーネントが本来の動作をすることを検証する複雑なテスト ハーネスのセットアップまで、さまざまです。与えられた状況に最適なものは、あなたが持っている要件と、実行したいブラックボックス/ホワイトボックステストの量に完全に依存します.

Erlang の優れた点の 1 つは、同時実行を透過的にできることです。次の例 (リストのリストの合計を並列化する関数) を考えてみましょう。

deep_sum(ListOfLists) ->
     Parent = self(),
     [spawn(fun() -> Parent ! lists:sum(List) end) || List <- ListOfLists],
     lists:sum([receive Sum -> Sum end || _ <- ListOfLists]).

通常、これは非常に単純な EUnit テスト ケースでテストします。

deep_sum_test() ->
     ?assertEqual(0,  deep_sum([0,  0,  0,  0])),
     ?assertEqual(40, deep_sum([10, 10, 10, 10]).

ここで、この機能に対するもう少し明示的な API があるとしましょう: 引数としてのプロセス プール:

deep_sum(Pool, ListOfLists) ->
     distribute_lists(Pool, ListOfLists),
     lists:sum([receive Sum -> Sum end || _ <- ListOfLists]).

distribute_lists(Pool, ListOfLists) -> distribute_lists(Pool, Pool, ListOfLists).

distribute_lists([P|Pool], All, [L|ListOfLists]) ->
     P ! {self(), L},
     distribute_lists(Pool, All, ListOfLists);
distribute_lists([], All, ListOfLists) ->
     distribute_lists(All, All, ListOfLists);
distribute_lists(_Pool, _All, []) ->
     ok.

これをテストするときは、このプロセス プールの偽造に対処する必要があります。

deep_sum_test() ->
     Pool = [spawn_link(fun() -> fake_pool(1) end) || _ <- lists:seq(1, 3)],
     ?assertEqual(4, deep_sum(Pool, [lists:seq(1, 3) || _ <- list:seq(1, 4)]),
     ?assertEqual(7, deep_sum(Pool, [lists:seq(1, 3) || _ <- list:seq(1, 7)]),
     [P ! stop || P <- Pool].

fake_pool(CannedResponse) ->
     receive
         {From, _L} -> From ! CannedResponse;
         stop -> ok
     end,
     fake_pool(CannedResponse).

おわかりのように、Erlang での並行性プログラムのテストは、さまざまな形を取ることができます。これらは非常に単純な例ですが、Erlang の組み込みの同時実行プリミティブを使用すると、適切なレベルで抽象化して、必要な種類のテスト ハーネスを非常に簡単に作成できます。

私は通常、TDD は並行コードをテストしているかどうかに関係なく直交していると考えているため、上記のテスト手法は通常の単体テストにも使用できます。

于 2009-01-16T12:26:18.920 に答える
1

並行コードのエラーは、さまざまな部分が実行される順序に応じて現れるため、コードの並行部分のバグを発見するためにテストに頼らないことが最善だと思います。このようなバグはすり抜けやすく、見つけるのが非常に困難です。たとえば、マルチスレッド環境でレイジー初期化を実装するための効率的な方法として広く引用され、使用されていたダブルロック手法が後でどのように壊れているかを確認してください。適切な抽象化と手法を使用して、コードの並行部分を「設計により」正しくすることをお勧めします。

于 2008-12-20T11:42:55.987 に答える
0

Diomidisが言ったことに加えて、並行コードにある程度の信頼を得る唯一の実際の方法は、絶えず変化する条件下で拡張テストを実行することです。それでも、それはそれらの条件下で失敗しなかったという証拠にすぎません。

于 2008-12-20T12:04:19.017 に答える
-1

これを使用して、モックでテストしたい部分を分離できます: http://github.com/charpi/erl_mock/tree/master

私は erl_mock の経験がありません。

于 2009-01-11T16:28:24.477 に答える