14

どのコードも副作用をもたらす可能性があります。ほとんどの場合、副作用は設計が悪いことやリファクタリングが必要であることを示している可能性がありますが、単体テストを行うときは、それに対してテストするのが難しいと感じています。次の例を検討してください。

[Test]
public void TrimAll_Removes_All_Spaces()
{
    // Arrange
    var testSubject = "A    string  with     lots   of     space";
    var expectedResult = "Astringwithlotsofspace";

    // Act
    var result = testSubject.TrimAll();

    // Assert
    Assert.AreEqual(expectedResult, result);
}

次の拡張機能をテストします。

public static string TrimAll(this string str)
{
    PokeAround();

    return str.Replace(" ", "");
}

テストはパスしますが、副作用に対するガードはありません。への呼び出しの効果は、PokeAroundまったく見過ごされます。

何が何であるかわからない場合、それは何でもかまいませんPokeAround! - それを防ぐテストをどのように書くのですか? それはまったく可能ですか?

明確化:PokeAroundテストを作成する時点でソースがあるため、完全に不明である可能性が非常に低いシナリオ について、いくつかのコメントがありました。しかし、私がこの質問をした理由は、後で追加される副作用を防ぐ方法を見つけるためでした. つまり、テストを書くとき、拡張メソッドは次のようになります。

public static string TrimAll(this string str)
{
    return str.Replace(" ", "");
}

テストに合格しました。すべて問題ありません。それから 1 か月後、私が休暇を取っているときに、同僚からPokeAround電話がかかってきました。彼がやったので、私はすでに書いたテストが失敗することを望んでいます。

4

10 に答える 10

14

これは、Working Effectively With Legacy Codeでセンシングと呼ばれるものです。つまり、テストされたメソッドの呼び出しの影響を感知します。

PokeAround が何であるかわからない場合は、何でもかまいません。

単体テストについて話しているので、これはほとんど真実ではありません.単体テストはホワイトボックステストであり、コードはチェックするためにそこにあります(あるはずです)。クローズドソースのサードパーティライブラリにある場合を除き、テストする必要がない場合、定義によりユニットテストを行うことはできません (おそらく機能/受け入れテストが必要ですが、それはまったく別の問題です...) .

更新:単体テスト済みのメソッドを将来変更しても、予期しない副作用が発生しないようにしたいですか? 私はあなたを思う

  1. できない、
  2. すべきではありません。

実際の(重要な)プログラムでメソッド呼び出しから副作用がないことを検出する賢明な方法がないため、できません。あなたが探しているのは、宇宙全体の状態がこれとこの小さなことを除いて変化していないことを確認することです. ささやかなプログラムから見ても、その宇宙は広大だ。メソッド呼び出しは、任意の数のローカル オブジェクトを作成/更新/削除し (その多くは単体テスト環境からは確認できません)、利用可能なローカル/ネットワーク ファイル システム上のファイルにアクセスし、DB 要求を実行し、リモート プロシージャ コールを行うことができます。 ..

そうするべきではありません。なぜなら、自分の変更の単体テストを処理するのは、将来の変更を行う同僚次第だからです。これが起こると信じていない場合は、単体テストの問題ではなく、人またはプロセスの問題があります。

于 2010-07-30T12:32:25.620 に答える
4

単体テストは、検証とチェックの武器庫の 1 つのツールにすぎないことに注意してください。その中には、同僚の「ポケアラウンド」をキャッチするものもあります。

  1. 単体テスト
  2. コード インスペクション/レビュー
  3. 契約による設計
  4. アサーション
  5. FindBugsChecker Frameworkなどの静的分析ツール(@NonNull、@Pure、および @ReadOnly はすべてロックです!)

他?

テストに合格しました。すべて問題ありません。それから 1 か月後の休暇中に、同僚が PokeAround コールを追加しました。彼がやったので、私はすでに書いたテストが失敗することを望んでいます。

あなたの同僚もテストを変えないだろうと思う理由は何ですか?

于 2010-12-10T03:08:16.897 に答える
2

PokeAroundが何であるかわからないことを考えると、それは何でもかまいません!-それを防ぐテストをどのように作成しますか?それは可能ですか?

この質問は疑わしいです。現実の世界ではこのような状況は起こりそうにありません。

  1. あなたはいつもPokeAroundが何であるかを知っています。ユニットテストです。ソースがあります。

    組織的な悪によってソースを読むことが禁止されている場合は、技術的な問題ではなく、組織的な問題があります。

    PokeAroundが何であるかわからない場合は、特に邪悪で成功を妨げている人々がいます。彼らは新しい仕事を必要としています。またはあなたはそうします。

  2. 副作用を観察できるように、このPokeAroundにはモックを使用する必要があります。

「後で追加される副作用から保護します。」

これは、不思議なコードの例ではありません。あなたはまだPokeAroundが何であるかを知っています。あなたはいつもPokeAroundが何であるかを知っています。

これが、回帰テストを行う理由です。 http://en.wikipedia.org/wiki/Regression_testing

それはまだユニットテストです。

  • スタンドアロンの単体テストでPokeAroundをテストします。

  • そして、PokeAroundのモックでPokeAroundを使用するものをテストします。

于 2010-07-30T12:41:39.310 に答える
2

私からの直接の経験はありませんが、これはあなたに興味があるかもしれません: コード コントラクト

http://research.microsoft.com/en-us/projects/contracts/

メソッドに [pure] 属性を設定し、実行時またはコンパイル時のチェックを通じて副作用がないことを強制する規定があります。また、印象的な他の制約のセットを指定して強制することもできます。

于 2010-07-30T12:50:37.427 に答える
1

私はこの言語でプログラミングしていませんが、元の文字列が変更されたかどうかをテストする方法の 1 つは次のとおりです。

[Test]
public void TrimAll_Removes_All_Spaces()
{
    // Arrange
    var testSubject = "A    string  with     lots   of     space";
    var testSubjectCopy = "A    string  with     lots   of     space";
    var expectedResult = "Astringwithlotsofspace";

    // Act
    var result = testSubject.TrimAll();

    // Assert
    Assert.AreEqual(expectedResult, result);

    // Check for side effects
    Assert.AreEqual(testSubjectCopy, testSubject);
}
于 2010-12-10T02:48:02.320 に答える
0

別の見方をすれば、このコードの単体テストを作成することで、コードの問題 (副作用) を認めざるを得なくなるということです。PokeAround次に、テスト可能にするためにさらに入力/状態が必要な場合は、おそらく独自の関数に移動するか、テストしようとしているコードからコードを再配置することにより、テスト可能になるようにコードを書き直し/再構築することができます.

于 2010-07-30T12:33:25.603 に答える
0

あなたができるかどうかわかりません。結局、それが意図した効果なのか副作用なのかは、設計者の意図次第です。

「副作用のある」コードは変更されていないと主張することをお勧めします。

また、 Jesterのようなツールが役立つかもしれませんが、あなたの例に違いはないと思います。

于 2010-07-30T12:34:31.917 に答える
0

副作用の意味によって異なります-つまり、おそらく PokeAround() は、実行する必要がある重要なことを行います。副作用をどのように分類しますか?

とにかく、単一の単体テストでそのような副作用を防ぐために私が認識している特定のテクノロジー/テクニックはありませんが、すべてのコードのテストカバレッジがある限り、望ましくない副作用はうまくいけば拾われます少なくとも 1 つのテスト。

BDD/統合テスト ツールは、(通常) 個々のクラス/メソッドだけでなく、より大きな機能領域をテストするため、これに対しても役立ちます。

注目すべきことの 1 つは、Design By Contract (DBC) です。これにより、事前条件と事後条件、および不変条件を指定できるため、メソッドが無効なパラメーターで呼び出されたり、無効な値を返したり、オブジェクトが無効な状態になったりすると、何らかのエラーがスローされます。

于 2010-07-30T12:35:04.767 に答える
0

いいえ、できません。副作用のテストは、どのテスト段階でも困難です。プロジェクトごとに異なります (製品開発、ツール開発、ビジネス アプリケーション開発、ゲーム開発など)。

完全な回帰テストがなければ、副作用は見つかりません。

典型的なプロジェクト (私が経験した) では、「この変更には何らかの副作用がありますか?」という質問は、プロジェクトの終盤 (稼働開始間近) や、すでに生産的なシステムにホット フィックスを追加したいときによく尋ねられます。回帰テストがなければ、唯一の (依然として危険な) 品質管理手段はコード レビューであることがわかりました。

それが役に立てば幸い。

于 2010-07-30T12:35:31.397 に答える
0

1 つの手法は、単体テストの順序をランダム化することです。テスト スイートがかなり包括的である場合、テストの順序をランダム化すると、予期しない副作用や、以前のテストの状態に誤って依存するテストなどが明らかになる可能性があります。

Google Test (C++ 用)はこれを行うことができます。この機能を持つ他のフレームワークは知りません。

于 2010-07-30T12:43:32.037 に答える