現在のスレッドがメインスレッドであることを表明する単体テスト


8

問題は、現在のスレッドがメインスレッドであることを表明する単体テストを記述することには意味があるのでしょうか。長所短所?

最近、サービスのコールバックのために現在のスレッドをアサートする単体テストを見ました。いいアイデアだとは思いませんが、もっと統合テストだと思います。私の意見では、単体テストはメソッドを個別にアサートする必要があり、サービスの利用者の性質については知らないはずです。

たとえば、iOSでは、このサービスのコンシューマーは、デフォルトでメインスレッドでコードを実行するための制約を持つUIであることが意図されています。


1
FWIW、コードでは、ユニットテストアサーションにするのではなく、ランタイムアサーションにします。
user1118321 2018

実際のテストケースというよりは、アサーションのように見えます。
whatsisname 2018

回答:


11

私のお気に入りの単体テストの原則を紹介しましょう。

次の場合、テストは単体テストではありません。

  1. データベースと通信します
  2. ネットワークを介して通信します
  3. それはファイルシステムに触れます
  4. 他の単体テストと同時に実行することはできません
  5. 実行するには、環境に特別なこと(構成ファイルの編集など)を行う必要があります。

ユニットテストルールのセット-Michael Feathers

ユニットテストで「メインスレッド」であると主張されている場合、4番のファウルを実行しないのは難しいことです。

これは厳しいように見えるかもしれませんが、単体テストはさまざまな種類のテストの1つにすぎないことを覚えておいてください。

ここに画像の説明を入力してください

これらの原則に違反することは自由です。ただし、テストをユニットテストと呼ばないでください。実際の単体テストでそれを入れて、それらを遅くしないでください。


ロガーをどのようにユニットテストしますか?あなたはIOをmocする必要がありますか?それは不愉快に思えます。
2018

1
ロガーに興味深いビジネスロジックがある場合は、@ opaを分離して単体テストします。ファイルシステムがあることを確認する退屈なコードは、単体テストを必要としません。
candied_orange 2018

@candied_orangeでは、内部ロジックを公開する必要がありますか?ロガーによって生成された文字列をファイルに出力する前に、それをプライベートメソッドに分離して他にどのように分離するのですか?
2018

@opaテストする必要がある場合は、独自のストリームを渡すことで実行できます。ユニットテストは、システムリソースではなくテストユニットです。
candied_orange

@candied_orange(ロガーが入力として出力するファイル名のみを取得する場合を除く)。ポイントは、「システムリソースではなくユニットテストのテストユニット」が間違っているということではなく、100%正解です。テストがファイルioをタッチしたときに、有効なユニットテストではないと言うほど、独断的になることはできません。そのファイル出力を読ん経由でロガーをテストするだけでユニットテストのために、民間の機能を公開することより方法が容易であるあなたが本当にすべき決してしません。それはIOのテストではなく、ロガーのテストです。それでも、クリーンなコードが必要な場合にファイルを読み取るのが最も簡単な方法です。
2018

5

スレッドがメインスレッドかどうかをテストする単体テストを作成する必要がありますか?依存しますが、おそらく良い考えではありません。

単体テストのポイントがメソッドの適切な機能を検証することを目的としている場合、この側面をテストすることは、ほぼ確実にこの側面をテストすることではありません。あなたがあなたが言ったように、メソッドの適切な機能はそれを実行しているスレッドに基づいて変わる可能性が低く、どのスレッドが使用されるかを決定する責任があるのは呼び出し側であり、メソッド自体。

ただし、メソッドが新しいスレッドを生成する場合、メソッド自体に大きく依存する可能性があるため、これが依存すると言う理由です。おそらくこれはあなたのケースではありません。

私のアドバイスは、このチェックを単体テストから除外し、この側面の適切な統合テストに移植することです。


2

サービスの消費者の性質を知らないはずです。

消費者がサービスを利用するとき、どの機能がどのように提供されるかについて明示的または黙示的な「契約」があります。コールバックが発生するスレッドの制限は、そのコントラクトの一部です。

あなたのコードは、「メインスレッド」で実行されるある種の「イベントエンジン」が存在するコンテキストで実行され、他のスレッドがイベントエンジンのスケジュールコードがメインスレッドで実行されることを要求する方法で実行されると思います。

ユニットテストの最も純粋なビューでは、すべての依存関係を絶対に模倣します。現実の世界ではそれを行う人はほとんどいません。テスト中のコードでは、少なくとも基本的なプラットフォーム機能の実際のバージョンを使用できます。

したがって、本当の質問IMOは、イベントエンジンとランタイムスレッディングサポートを、単体テストが依存できるかどうかにかかわらず許可される「基本的なプラットフォーム機能」の一部と見なすことですか。その場合、コールバックが「メインスレッド」で発生するかどうかを確認することは完全に理にかなっています。イベントエンジンとスレッドシステムをモックする場合は、モックを設計して、コールバックがメインスレッドで発生したかどうかを判断できるようにします。イベントエンジンをモックしているが、ランタイムスレッディングサポートはモックしていない場合は、モックイベントエンジンが実行されているスレッドでコールバックが発生するかどうかをテストする必要があります。


1

それが魅力的なテストになる理由がわかります。しかし、実際には何をテストするのでしょうか?私たちは機能を持っていると言います

showMessage(string m)
{
    new DialogueBox(m).Show(); //will fail if not called from UI thread
}

これをテストして非UIスレッドで実行すると、エラーがスローされることがわかります。ただし、実際にそのテストが何らかの値を持つためには、関数が非UIスレッドで実行されたときに実行する特定の動作が必要です。

showMessage(string m)
{
    try {
        new DialogueBox(m).Show(); //will fail if not called from UI thread
    }
    catch {
        Logger.Log(m); //alternative display method
    }
}

今、私はテストする必要がありますが、この種類の代替手段が実際の生活に役立つかどうかは不明です


1

コールバックが、UIスレッドやメインスレッド以外で起動してもスレッドセーフではないことが文書化されている場合、コードモジュールの単体テストでは、このコールバックの呼び出しを確実にするために完全に妥当と思われます。安全を保証するためにOSまたはアプリケーションフレームワークが文書化されているスレッドの外部で作業することにより、スレッドセーフではない処理を行っていません。


1

アレンジ

アサートを

ユニットテストはそれが特定のスレッド上にあることを主張するべきですか?いいえ、その場合はユニットをテストしていないため、統合テストでさえありません。ユニットで行われている構成については何も言われていないからです...

テストが実行されているスレッドをアサートすることは、テストサーバーの名前をアサートすること、またはテストメソッドの名前をアサートすることに似ています。

さて、特定のスレッドでテストを実行するようにArrangeするのは理にかなっているかもしれませんが、スレッドに基づいて特定の結果を確実に返すようにしたい場合のみです。


1

メインスレッドでのみ正しく実行することが記載されている関数があり、その関数がバックグラウンドスレッドで呼び出された場合、それは関数の障害ではありません。障害は呼び出し側の障害であるため、単体テストは無意味です。

関数がメインスレッドで正しく実行され、バックグラウンドスレッドで実行されたときにエラーを報告するようにコントラクトが変更された場合、そのためのユニットテストを記述できます。バックグラウンドスレッドで、ドキュメントどおりに動作することを確認します。これで、ユニットテストができましたが、メインスレッドで実行されることを確認するユニットテストはありません。

関数の最初の行で、メインスレッドで実行することをアサートすることを強くお勧めします。


1

単体テストは、正しい使用方法ではなく、上記の関数の正しい実装をテストしている場合にのみ役立ちます。単体テストでは、コードベースの残りの部分にある別のスレッドから関数が呼び出されないことはできないためです。 t前提条件に違反するパラメーターを指定して関数を呼び出すことはできないと言います(技術的には、テストしようとしているのは、基本的に使用法の前提条件の違反です。これは、テストを行うことができないため、効果的にテストすることはできません。 tコードベースの他の場所がそのような関数をどのように使用するかを制限します;ただし、前提条件の違反が適切なエラー/例外になるかどうかをテストできます)。

これは、他の人が指摘しているように、関連する関数自体の実装における私にとっての主張のケースです。あるいは、関数がスレッドセーフであることを確認することはさらに洗練されています(ただし、一部のAPIを操作する場合、これは必ずしも実用的ではありません)。

また、余談ですが、すべてのケースで「メインスレッド」!=「UIスレッド」です。多くのGUI APIはスレッドセーフではありません(そして、スレッドセーフGUIキットを作成するのは難しいです)が、それは、アプリケーションのエントリポイントがあるスレッドと同じスレッドからそれらを呼び出す必要があるという意味ではありません。これは、関連するUI関数内にアサーションを実装して「UIスレッド」と「メインスレッド」を区別する場合にも役立ちます。たとえば、アプリケーションのメインエントリポイント(ではなく、少なくとも、実装が本当に関連するものにのみ適用している仮定/使用制限の量を減らします。

スレッドセーフティは、実際には以前の私のチームの「問題」の原因であり、私たちの特定のケースでは、スレッドセーフの中で最も逆効果的な「マイクロ最適化」というラベルを付け、すべての種類のメンテナンスコストよりも多くのメンテナンスコストをかけました。手書きのアセンブリ。単体テストではかなり包括的なコードカバレッジがあり、かなり洗練された統合テストがありましたが、テストを回避したソフトウェアでデッドロックと競合状態に遭遇しました。そしてそれは、開発者が関数呼び出しのチェーンで発生する可能性のあるすべての副作用を認識せずに、無計画にマルチスレッド化されたコードであり、投げるだけで後からそのようなバグを修正できるというかなり単純な考えがあるためです左右にロックし、

マルチスレッディングを信用していない古い学校のタイプとは逆の方向に歪んでいて、それを採用するのは本当に遅れており、正しさがパフォーマンスを打ち負かして、今あるすべてのコアをめったに使用しないようにしました。純粋な関数、不変のデザイン、永続的なデータ構造のように、レース状態やデッドロックを心配することなく、ハードウェアを完全に活用することができました。2010年くらいまでずっと、私はマルチスレッド化を嫌っていましたが、スレッドセーフの理由から取るに足らない領域でのいくつかの並列ループを除いて、情熱を持って熱意を持っていました。以前のチームでマルチスレッディングに悩まされた製品。

私にとって、最初にマルチスレッド化し、後でバグを修正する方法は、最初からマルチスレッド化を嫌いになるほどにマルチスレッド化する恐ろしい戦略です。設計が確実なスレッドセーフであり、それらの実装が同様の保証のある関数(例:純粋な関数)のみを使用することを確認するか、マルチスレッドを回避します。それは少し独断的に出くわすかもしれませんが、それはテストを回避する後知恵の中で再現するのが難しい問題を発見する(またはさらに悪いことに、発見しない)よりも優れています。ロケットエンジンを最適化しても、宇宙への旅の途中で予期せず突然爆発しやすくなる場合は、意味がありません。

スレッドセーフではないコードを使用せざるを得ない場合は、ユニット/統合テストでそれほど解決する必要のある問題だとは思いません。アクセス制限することが理想的です。GUIコードがビジネスロジックから分離されている場合、それを作成するスレッド/オブジェクト以外からのそのような呼び出しへのアクセスを制限する設計を実施できる可能性があります*。これは、私にとって理想的な遠いモードです。他のスレッドがこれらの関数を呼び出せないようにするのではなく、それらを呼び出せないようにすることです。

  • はい、私は、コンパイラーがあなたを保護することができないところで、通常あなたが強制するどんなデザイン制限の周りにも常に方法があることを理解しています。私は実際に話しているだけです。「GUIスレッド」オブジェクトなどを抽象化できる場合、GUIオブジェクト/関数にパラメーターを渡すのはそれだけであり、そのオブジェクトが他のスレッドにアクセスできないように制限することができます。もちろん、それを迂回して深く掘り下げて、そのようなフープを回避して上記のGUI関数/オブジェクトを他のスレッドに渡して呼び出すことができるかもしれませんが、少なくともそこには障壁があり、それを実行する誰にでも「ばか「そして、少なくとも、設計が明らかに制限しようとしていたものの抜け穴を明確に迂回して探すために、間違っているわけではありません。:-Dそれは
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.