テスト可能なコードはより良いコードですか?


103

私は自分のコードで定期的にユニットテストを書く習慣を身につけようとしていますが、最初に読んだのはテスト可能なコードを書くことですこの質問は、テスト可能なコードを作成するという固い原則に触れていますが、テストの作成をまったく計画せずに、それらの設計原則が有益である(少なくとも害がない)かどうかを知りたいです。明確にするために-テストを書くことの重要性を理解しています。これは、それらの有用性に関する質問ではありません。

私の混乱を説明するために、この質問に影響を与えた作品で、ライターは現在の時刻をチェックし、時刻に応じて値を返す関数の例を示します。作成者は、内部で使用するデータ(時間)を生成するため、テストが困難になるため、これを不良コードと指摘します。私にとっては、しかし、引数として時間を渡すことはやり過ぎのようです。ある時点で、値を初期化する必要がありますが、なぜ消費に最も近いのではありませんか?さらに、私の考えでは、このメソッドの目的は、現在の時刻に基づいて何らかの値を返すことであり、この目的を変更できるかどうかを示すパラメータにすることです。これと他の質問は、テスト可能なコードが「より良い」コードと同義語かどうか疑問に思います。

テストがない場合でも、テスト可能なコードを書くことはまだ良い習慣ですか?


テスト可能なコードは実際にはより安定していますか?重複として提案されています。ただし、その質問はコードの「安定性」に関するものですが、読みやすさ、パフォーマンス、結合など、他の理由でもコードが優れているかどうかについてより広くお尋ねしています。


24
この関数には、べきと呼ばれる時間を渡す必要がある特別なプロパティがあります このような関数は、指定された引数値で呼び出されるたびに同じ結果を生成ます。これにより、テストしやすくなるだけでなく、構成しやすく、推論しやすくなります。
ロバートハーヴェイ

4
「より良いコード」を定義できますか?「維持可能」という意味ですか?、「IOCコンテナなしの使いやすいマジック」ですか?
k3b

7
実際のシステム時間を使用してからタイムゾーンオフセットを変更したため、テストが失敗したことはないでしょう。
アンディ

5
テストできないコードよりも優れています。
Tulainsコルドバ

14
私はその冪等を呼び出すことはありません@RobertHarvey、私はそれがだと言うでしょう参照透明性:あればfunc(X)リターンは"Morning"、その後のすべての出現箇所の交換func(X)"Morning"プログラムを変更しません(つまり、呼び出しが。func値を返す以外何もしません)。dem等性とは、それfunc(func(X)) == X(型が正しくない)、またはfunc(X); func(X);同じ副作用func(X)(ただし、副作用はない)を実行することを意味します
-Warbo

回答:


116

単体テストの一般的な定義に関しては、ノーと言います。テストフレームワークに合わせてツイストする必要があるため、単純なコードが複雑になっているのを見てきました(たとえば、インターフェイスとIoCがどこでも、インターフェイスコールの層と、マジックによって渡されるはずのデータを追跡するのが難しくなります)。理解しやすいコードと単体テストが簡単なコードのどちらかを選択できるので、毎回保守可能なコードを使用します。

これは、テストすることではなく、ツールを自分に合うように適合させることを意味し、逆ではありません。他にもテストする方法があります(ただし、理解しにくいコードは常に悪いコードです)。たとえば、粒度の低い単体テスト(たとえば、ユニットはメソッドではなくクラスであるというMartin Fowlerの態度)を作成したり、代わりに自動化された統合テストでプログラムをヒットすることができます。これは、テストフレームワークが緑色のチェックマークで点灯するほどきれいではないかもしれませんが、プロセスのゲーミフィケーションではなく、テストされたコードです。

コード間で適切なインターフェイスを定義し、コンポーネントのパブリックインターフェイスを実行するテストを作成することにより、コードを維持しやすく、単体テストに適したものにすることができます。または、より良いテストフレームワークを取得することもできます(実行時に関数を置き換えてモックを作成し、モックを使用してコードをコンパイルする必要はありません)。優れた単体テストフレームワークを使用すると、実行時にシステムのGetCurrentTime()機能を独自の機能に置き換えることができるため、テストツールに合わせてこれに人工的なラッパーを導入する必要はありません。


3
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
世界エンジニア

2
あなたの最後の段落で説明されていることを行うことができる少なくとも1つの言語、Python with Mockを知っていることは注目に値すると思います。モジュールのインポートが機能する方法のために、キーワード以外のほとんどすべてを、標準のAPIメソッド/クラス/などでさえもモックに置き換えることができます。それは可能ですが、そのような柔軟性をサポートするような方法で言語を設計する必要があるかもしれません。
jpmc26

6
「テスト可能なコード」と「テストフレームワークに合わせてコードを[ツイスト]」の間に違いがあると思います。「ツイスト」コードが悪いこと、そして良いインターフェースを備えた「テスト可能な」コードが良いことに同意することを除いて、私はこのコメントでどこに行くのか分かりません。
ブライアンオークリー

2
記事のコメントで自分の考えのいくつかを表現しました(ここでは拡張コメントは許可されていないため)、チェックしてください!明確にするために:私は言及された記事の著者です:)
セルゲイコロディ

@BryanOakleyに同意する必要があります。「テスト可能なコード」は、懸念事項が分離されていることを示しています。他の側面から干渉されることなく、側面(モジュール)をテストすることが可能です。これは、「プロジェクトサポートの特定のテスト規則の調整」とは異なると思います。これは設計パターンに似ています:強制されるべきではありません。デザインパターンを適切に利用するコードは、強力なコードと見なされます。テスト原則にも同じことが当てはまります。コードを「テスト可能」にした結果、プロジェクトのコードが過度にねじれた場合、何か間違ったことをしていることになります。
ビンスエミー

68

テストがない場合でも、テスト可能なコードを書くことはまだ良い習慣ですか?

まず最初に、テストがないことは、コードがテスト可能かどうかよりもはるかに大きな問題です。単体テストがないということは、コード/機能が完了していないことを意味します。

ちなみに、テスト可能なコードを書くことは重要だとは言いません。柔軟なコードを書くことが重要です。柔軟性のないコードはテストするのが難しいため、アプローチと人々がそれを呼ぶものには多くの重複があります。

だから、私にとっては、コードの記述には常に一連の優先事項があります。

  1. 動作させる -コードが必要なことを実行しない場合、それは価値がありません。
  2. 保守可能にします-コードが保守可能でない場合、すぐに動作を停止します。
  3. コードを柔軟にする -コードが柔軟でない場合、ビジネスが必然的にやって来て、コードがXYZを実行できるかどうかを尋ねたときに、コードが機能しなくなります。
  4. 速くしてください -基本的な許容レベルを超えて、パフォーマンスはグレービーです。

単体テストは、コードの保守性に役立ちますが、ある程度までです。コードを読みにくくしたり、単体テストを機能させるために脆弱にしたりすると、生産性が低下します。「テスト可能なコード」は一般に柔軟性のあるコードであるため、優れていますが、機能や保守性ほど重要ではありません。現在のように、柔軟性を高めることは良いことですが、コードをより適切かつ複雑に使用するのを難しくすることにより、保守性を損ないます。保守性がより重要であるため、テストしにくい場合でも、通常は単純なアプローチに向かってエラーを出します。


4
テスト可能なものと柔軟なものとの関係を指摘してください。これにより、問題全体が理解しやすくなります。柔軟性はコードの適応を可能にしますが、必然的に理解するのに少し抽象的で直感的ではなくなりますが、それは利益のために価値のある犠牲です。
WannabeCoder

3
そうは言っても、ユニットテストフレームワークが直接アクセスできるようにするために、パブリックまたはパッケージレベルに強制されるプライベートなメソッドをよく見ます。理想的なアプローチとはほど遠い。
15

4
@WannabeCoderもちろん、最終的に時間を節約する場合にのみ柔軟性を追加する価値があります。そのため、インターフェイスに対してすべてのメソッドを作成するわけではありません。ほとんどの場合、最初から柔軟性を取り入れすぎるよりも、コードを書き換える方が簡単です。YAGNIは依然として非常に強力な原則です。「必要ない」ものが何であれ、それを遡及的に追加しても、事前に実装するよりも平均して多くの作業が得られないことを確認してください。私の経験では柔軟性に最も問題があるのは、YAGNIに従わないコードです。
ルアーン

3
「単体テストがないということは、コード/機能が完了していないことを意味します」-正しくありません。「完了の定義」は、チームが決定するものです。ある程度のテストカバレッジを含む場合と含まない場合があります。しかし、テストがなければ機能を「完了」できないという厳しい要件はどこにもありません。チームはテストを要求することも、要求しないこともできます。
アロス

3
@Telastyn 10年以上の開発の中で、ユニットテストフレームワークを義務付けたチームは1つもありませんでした。1つの場所では、作成中の機能をテストする方法の文書が必要でした。それでおしまい。おそらく私は不運ですか?私はアンチユニットテストではありません(真剣に、私はSQA.SEサイトを改造します、私は非常にプロのユニットテストです!)。
corsiKa

50

はい、それは良い習慣です。その理由は、テスト容易性はテストのためではないからです。それはそれがもたらす明快さと理解可能性のためです。

誰もテスト自体を気にしません。足元を常にチェックしなくても完璧なコードを書くのに十分な才能がないため、大規模な回帰テストスイートが必要なのは悲しい事実です。できれば、テストの概念は不明であり、このすべてが問題になることはありません。できればいいのに。しかし、経験上、ほとんどすべての人ができないことを示しているため、ビジネスコードの作成に時間を割いたとしても、コードをカバーするテストは良いことです。

テストを行うことで、テストとは独立してビジネスコードがどのように改善されますか?機能が適切であることを簡単に実証できるユニットに分割することを強制することにより。また、これらのユニットは、書きたいと思われるものよりも簡単に入手できます。

あなたの時間の例は良い点です。現在の時刻を返す関数のみを持っている限り、それをプログラム可能にしても意味がないと思うかもしれません。これを正しくするのはどれほど難しいでしょうか?しかし、必然的に、プログラムは他のコードでこの関数を使用するため、そのコードを異なる条件で(異なる時間を含む)テストする必要があります。あなたは1行の疑いためではない-したがって、それはあなたの関数が返す時間を操作できるようにすると良いでしょうcurrentMillis()コールを、しかし、あなたは確認する必要があるため、発信者制御された状況の下でそのコールのを。したがって、コードをテスト可能にすることは、それだけでは注意を払う価値がないように思えても便利です。


別の例は、あるプロジェクトのコードの一部を別の場所に引き出したい場合です(何らかの理由で)。機能のさまざまな部分が互いに独立しているほど、必要な機能だけを簡単に抽出できます。
ヴァレンテリー

10
Nobody cares about the tests themselves - 私がやります。テストは、コードが何を行うのかについてのより良いドキュメントであり、コメントやreadmeファイルよりも優れていると思います。
jcollum

私はしばらくの間テストプラクティスについてゆっくり読んでいます(何とかユニットテストをまだまったくしていない人として)、制御された環境での呼び出しの検証に関する最後の部分、および付属のより柔軟なコードそれは、あらゆる種類のものを所定の位置にカチッとさせました。ありがとうございました。
plast1k

12

ある時点で、値を初期化する必要がありますが、なぜ消費に最も近いのではありませんか?

そのコードは、内部で生成された値とは異なる値で再利用する必要がある場合があるためです。パラメーターとして使用する値を挿入する機能により、「今」だけでなく(コードを呼び出すときの「今」という意味で)いつでも好きなときにそれらの値を生成できます。

コードを実質的にテスト可能にするということは、2つの異なるシナリオ(運用とテスト)で(最初から)使用できるコードを作成することを意味します。

基本的に、テストがない場合にコードをテスト可能にするインセンティブはないと主張できますが、再利用可能なコードを書くことには大きな利点があり、2つは同義語です。

さらに、私の考えでは、このメソッドの目的は、現在の時刻に基づいて値を返すことです。これをパラメーターにすると、この目的を変更できる/すべきであると示唆することになります。

また、このメソッドの目的は時間値に基づいて値を返すことであり、「今」に基づいて値を生成する必要があると主張することもできます。そのうちの1つはより柔軟性があり、そのバリアントの選択に慣れると、時間の経過とともにコードの再利用率が上がります。


10

このように言うのは馬鹿げているように思えるかもしれませんが、コードをテストできるようにしたい場合は、テスト可能なコードを書く方が良いでしょう。あなたが尋ねる:

ある時点で、値を初期化する必要がありますが、なぜ消費に最も近いのではありませんか?

なぜなら、あなたが参照している例では、そのコードがテスト不能になっているからです。1日の異なる時間にテストのサブセットのみを実行する場合を除きます。または、システムクロックをリセットします。またはその他の回避策。これらはすべて、単にコードを柔軟にするよりも悪いことです。

柔軟性に欠けるだけでなく、問題の小さなメソッドには2つの責任があります。(1)システム時間を取得し、(2)それに基づいて値を返すことです。

public static string GetTimeOfDay()
{
    DateTime time = DateTime.Now;
    if (time.Hour >= 0 && time.Hour < 6)
    {
        return "Night";
    }
    if (time.Hour >= 6 && time.Hour < 12)
    {
        return "Morning";
    }
    if (time.Hour >= 12 && time.Hour < 18)
    {
        return "Afternoon";
    }
    return "Evening";
}

責任をさらに細分化することは理にかなっています。そのため、制御できない部分(DateTime.Now)が残りのコードに与える影響が最も少なくなります。そうすることで、上記のコードが簡単になり、体系的にテストできるという副作用があります。


1
したがって、必要なときに「夜」の結果が得られることを確認するには、早朝にテストする必要があります。それは難しいです。2016年2月29日に日付の処理が正しいことを確認したいとします...そして、一部のiOSプログラマー(おそらく他のプログラマー)は、年初の前後に物事を台無しにする初心者のミスに悩まされています。そのためのテスト。そして経験から、私は2020年、2月2日に日付処理を確認します
gnasher729

1
@ gnasher729まさに私のポイント。「このコードをテスト可能にする」は、多くの(テスト)問題を解決できる簡単な変更です。テストを自動化したくない場合、コードは現状のままで通用します。しかし、それが「テスト可能」になればより良いでしょう。
エリックキング

9

確かにコストがかかりますが、一部の開発者はそれを支払うことに非常に慣れているため、そこにあるコストを忘れてしまいました。たとえば、1つではなく2つのユニットがあり、追加の依存関係を初期化および管理するために呼び出しコードが必要であり、GetTimeOfDayよりテストしやすい一方で、新しいボートをテストする同じボートに戻っていますIDateTimeProvider。優れたテストを実施している場合、通常はコストよりもメリットのほうが大きいというだけです。

また、ある程度、テスト可能なコードを書くことで、コードをより保守可能な方法で設計することができます。新しい依存関係管理コードは面倒なので、可能な限りすべての時間依存関数をグループ化する必要があります。これは、たとえば、時間境界上でページをロードし、一部の要素を前の時間を使用してレンダリングし、一部の要素を後の時間を使用して使用するなどのバグを軽減および修正するのに役立ちます。また、現在の時刻を取得するためのシステムコールの繰り返しを回避することにより、プログラムを高速化することもできます。

もちろん、これらのアーキテクチャの改善は、誰かが機会に気付き、それを実装することに大きく依存しています。ユニットに非常に注意を向けることの最大の危険の1つは、全体像を見失うことです。

多くの単体テストフレームワークでは、実行時に模擬オブジェクトにモンキーパッチを適用できるため、混乱することなくテスト容易性の利点を得ることができます。私はC ++でそれを見たことがあります。テスタビリティコストに見合わないような状況では、その能力を調べてください。


+1-ユニットテストの記述を容易にするために、設計とアーキテクチャを改善する必要があります。
BЈовић

3
+-重要なコードのアーキテクチャ。より簡単なテストは幸せな副作用です。
gbjbaanb

8

テスタビリティのコンテキスト以外では、テスタビリティに寄与するすべての特性が望ましいわけではない可能性があります-たとえば、引用する時間パラメータのテストに関連しない正当化を考えるのに苦労していますが、テスタビリティに寄与する特性を広く言えばまた、テスト容易性に関係なく、優れたコードに貢献します。

大まかに言って、テスト可能なコードは順応性のあるコードです。それは小さな、離散した、まとまりのある塊であるため、個々のビットを再利用のために呼び出すことができます。よく整理され、名前が付けられています(一部の機能をテストできるようにするには、命名にもっと注意を払います。テストを作成していなければ、1回限りの関数の名前はそれほど重要ではありません)。(あなたの時間の例のように)よりパラメトリックになる傾向があるので、元の意図された目的以外のコンテキストから使用するためにオープンです。それは乾燥しているので、混乱が少なく、理解しやすいです。

はい。 テストに関係なく、テスト可能なコードを作成することをお勧めします。


DRYであることに同意しません-メソッドGetGetTimeをメソッドMyGetCurrentTimeでラップすると、テストツーリングを支援する以外の利点はなく、OS呼び出しが非常に繰り返されます。これは最も単純な例であり、実際にはさらに悪化します。
gbjbaanb

1
「恩恵なしでOS呼び出しを繰り返す」-1つのクロックでサーバーで実行し、別のタイムゾーンのawsサーバーに話しかけるまで、コードが壊れるまで、すべてのコードを実行する必要があります。それを更新して、代わりにUTCを返すMyGetCurrentTimeを使用します。; クロックスキュー、夏時間、およびOS呼び出しを盲目的に信頼するのは得策ではないかもしれない他の理由があります。
アンドリューヒル

8

コードが実際に機能することを証明できるようにするには、テスト可能なコードを書くことが重要です

コードを特定のテストフレームワークに適合させるためだけに、コードを悪質なゆがみにワープすることについての否定的な感情に同意する傾向があります。

一方で、ここにいる誰もが、何らかの点で、対処しなければならない凶悪な1,000行の魔法の機能に対処する必要があり、事実上、1つ以上の不明瞭な、明らかな依存関係はどこか別の場所(または依存関係を視覚化するのがほぼ不可能な場所自体)であり、定義上ほとんどテストできません。私の意見では、テストフレームワークが誇張されたという概念(メリットがないわけではありません)を、低品質でテスト不能なコードを書くための無料ライセンスと見なすべきではありません。

テスト駆動開発の理想は、たとえば、単一責任の手順を書くようにあなたを押し付ける傾向があり、それは間違いなく良いことです。個人的には、単一の責任、単一の真実のソース、制御されたスコープ(異常なグローバル変数なし)を購入し、脆弱な依存関係を最小限に抑えれば、コードテスト可能になります。特定のテストフレームワークでテストできますか?知るか。ただし、テストフレームワークが適切なコードに合わせて調整する必要があるのか​​もしれませんが、その逆ではありません。

しかし、明確にするために、非常に賢い、または非常に長く、かつ/または他の人間に容易に理解されないほど相互依存するコードは、良いコードではありません。また、偶然にも、簡単にテストできるコードではありません。

だから私の要約に近づいて、テスト可能なコードはより良いコードですか?

わからない、多分知らない。ここの人々はいくつかの有効なポイントを持っています。

しかし、より良いコードはテスト可能なコードになる傾向があると信じています。

そして、もしあなたが真剣な努力で使用する真剣なソフトウェアについて話しているなら、あなたの雇用主や顧客のお金で未テストのコードを配送することはあなたにとって最も責任のあることではありません。

また、一部のコードは他のコードよりも厳しいテストを必要とし、そうでないふりをするのは少しばかげていることも事実です。シャトルの重要なシステムとインターフェイスするメニューシステムがテストされていない場合、スペースシャトルの宇宙飛行士になりたいと思いませんか?または、原子炉内の温度を監視するソフトウェアシステムがテストされていない原子力発電所の従業員ですか?一方、単純な読み取り専用レポートを生成するコードには、ドキュメントと千単位のテストが満載のコンテナトラックが必要ですか?しないことを願っています。ただ言って...


1
「より良いコードはテスト可能なコードでもある傾向があります」これが鍵です。テスト可能にしても、改善されるわけではありません。作ることは、より良い、多くの場合、それがテスト可能になり、そしてテストは、多くの場合、あなたがそれを改善するために使用できる情報を与えるが、テストの単なる存在は品質を意味するものではありません。また、(まれ)例外があります。
アナキシマンダー

1
まさに。反対のことを考えてください。テストできないコードの場合、テストされていません。テストされていない場合、実際の状況以外で機能するかどうかをどのように確認しますか?
pjc50

1
すべてのテストは、コードがテストに合格することを証明しています。そうでなければ、ユニットテストされたコードにはバグがなく、そうではないことがわかります。
wobbily_col

@anaximanderまさに。少なくともテストの存在は禁忌である可能性があり、すべてのチェックボックスをチェックすることに焦点を合わせている場合、コードの品質が低下します。「機能ごとに少なくとも7つのユニットテスト」"小切手。" しかし、コードが高品質のコードであれば、テストが簡単になると本当に信じています。
クレイグ

1
...しかし、テストから官僚制度を作ることは完全に無駄であり、有用な情報や信頼できる結果を生み出さない可能性があります。とにかく; 誰かがSSL Heartbleedのバグをテストしてくれたらいいのにと思いますよね?またはApple gotoがバグを失敗しますか?
クレイグ

5

私にとっては、しかし、引数として時間を渡すことはやり過ぎのようです。

あなたは正しいです、そしてモックを使用すると、コードをテスト可能に、時間を渡すことを避けることができます(しゃれの意図は未定義)。サンプルコード:

def time_of_day():
    return datetime.datetime.utcnow().strftime('%H:%M:%S')

ここで、うるう秒の間に何が起こるかをテストしたいとしましょう。あなたが言うように、これを過剰な方法でテストするには、(本番)コードを変更する必要があります:

def time_of_day(now=None):
    now = now if now is not None else datetime.datetime.utcnow()
    return now.strftime('%H:%M:%S')

Pythonがうるう秒をサポートしている場合、テストコードは次のようになります。

def test_handle_leap_second(self):
    actual = time_of_day(
        now=datetime.datetime(year=2015, month=6, day=30, hour=23, minute=59, second=60)
    expected = '23:59:60'
    self.assertEquals(actual, expected)

これをテストすることはできますが、コードは必要以上に複雑であり、テストはまだほとんどの本番コードが使用するコードブランチを確実に実行できません(つまり、値を渡さないnow)。これを回避するには、モックを使用します。元の製品コードから開始:

def time_of_day():
    return datetime.datetime.utcnow().strftime('%H:%M:%S')

テストコード:

@unittest.patch('datetime.datetime.utcnow')
def test_handle_leap_second(self, utcnow_mock):
    utcnow_mock.return_value = datetime.datetime(
        year=2015, month=6, day=30, hour=23, minute=59, second=60)
    actual = time_of_day()
    expected = '23:59:60'
    self.assertEquals(actual, expected)

これにはいくつかの利点があります。

  • 依存関係とはtime_of_day 無関係にテストしています。
  • 実動コードと同じコードパスをテストしています。
  • 量産コードは可能な限り単純です。

サイドノートでは、将来のモックフレームワークがこのようなことを簡単にすることを期待しています。たとえば、モックされた関数を文字列として参照する必要があるため、time_of_day別のソースの使用を開始するときにIDEで自動的に変更することは簡単ではありません。


参考:デフォルトの引数が間違っています。定義されるのは一度だけなので、関数は常に最初に評価された時間を返します。
-ahruss

4

よく書かれたコードの品質は、変更に対して堅牢であるということです。つまり、要件の変更が発生すると、コードの変更は比例するはずです。これは理想的です(常に達成されるとは限りません)が、テスト可能なコードを書くことでこの目標に近づくことができます。

なぜそれが私たちを近づけるのに役立つのですか?本番環境では、コードは本番環境内で動作します。これには、他のすべてのコードの統合や相互作用が含まれます。単体テストでは、この環境の大部分を一掃します。テスト変更であるため、コードは変更に対して堅牢です。生産で使用する場合とは異なる入力(モック、実際には渡されない可能性のある不正な入力など)で、ユニットを異なる方法で使用しています。

これにより、システムで変更が発生した日のコードが準備されます。私たちの時間計算は、タイムゾーンに基づいて異なる時間を取る必要があるとしましょう。これで、その時間を渡すことができ、コードを変更する必要がなくなりました。ある時間を渡したくなく、現在の時間を使用したい場合は、デフォルトの引数を使用できます。コードはテスト可能であるため、変更に対して堅牢です。


4

私の経験から、プログラムを構築する際に行う最も重要で最も広範囲にわたる決定の1つは、コードをユニットに分解する方法です(「ユニット」は最も広い意味で使用されます)。クラスベースのオブジェクト指向言語を使用している場合、プログラムをいくつかのクラスに実装するために使用されるすべての内部メカニズムを分割する必要があります。次に、各クラスのコードをいくつかのメソッドに分割する必要があります。一部の言語では、コードを関数に分割する方法が選択されます。または、SOAを行う場合は、構築するサービスの数と各サービスに何を入れるかを決定する必要があります。

選択した内訳は、プロセス全体に多大な影響を及ぼします。適切な選択を行うと、コードを記述しやすくなり、テストとデバッグを開始する前であってもバグが少なくなります。変更と保守が容易になります。おもしろいことに、適切な内訳を見つけたら、通常は下手な内訳よりもテストしやすいことがわかります。

これはなぜですか?すべての理由を理解して説明できるとは思わない。しかし、1つの理由は、適切な内訳とは常に、実装単位に適度な「粒度」を選択することを意味することです。機能やロジックを1つのクラス/メソッド/関数/モジュール/などに詰め込みたくありません。これにより、コードの読み取りと書き込みが簡単になりますが、テストも簡単になります。

ただし、それだけではありません。優れた内部設計とは、各実装ユニットの予想される動作(入力/出力/など)を明確かつ正確に定義できることを意味します。これはテストに重要です。通常、優れた設計とは、実装の各ユニットが他のユニットに中程度の数の依存関係を持つことを意味します。これにより、他の人がコードを読んで理解しやすくなりますが、テストも簡単になります。理由は続く。他の人が私にはできないより多くの理由を明確に説明できるかもしれません。

あなたの質問の例に関して、「良いコード設計」とは、すべての外部依存関係(システムクロックへの依存関係など)を常に「注入」する必要があると言うこととは考えられません。これは良いアイデアかもしれませんが、ここで説明していることとは別の問題であり、その長所と短所については掘り下げません。

ちなみに、現在の時刻を返すシステム関数を直接呼び出したり、ファイルシステムを操作したりしても、コードを単体で単体テストできないというわけではありません。トリックは、システム関数の戻り値を偽造できるようにする特別なバージョンの標準ライブラリを使用することです。他の人がこの手法に言及しているのを見たことはありませんが、多くの言語や開発プラットフォームで行うのはかなり簡単です。(できれば、あなたの言語ランタイムはオープンソースであり、簡単に構築できます。コードの実行にリンク手順が含まれる場合、リンク先のライブラリを簡単に制御できることを願っています。)

要約すると、テスト可能なコードは必ずしも「良い」コードではありませんが、「良い」コードは通常テスト可能です。


1

あなたが一緒に行くしている場合はSOLID原則あなたがこれを拡張する場合は特に、良い側になりますKISSDRY、およびYAGNI

私にとって一つ欠けている点は、メソッドの複雑さの点です。シンプルなゲッター/セッターメソッドですか?その場合、テストフレームワークを満たすためにテストを記述するだけでは時間の無駄になります。

データを操作するより複雑なメソッドであり、内部ロジックを変更する必要がある場合でも機能することを確認したい場合は、テストメソッドの素晴らしい呼び出しになります。多くの場合、数日/数週間/数か月後にコードの一部を変更する必要があり、テストケースができて本当にうれしかったです。メソッドを最初に開発したとき、テストメソッドでテストしましたが、確実に機能するはずです。変更後、私の主要なテストコードはまだ機能していました。ですから、私の変更が本番環境の古いコードを壊さないことは確かでした。

テストを記述するもう1つの側面は、他の開発者にメソッドの使用方法を示すことです。多くの場合、開発者はメソッドの使用方法と戻り値がどうなるかの例を検索します。

ちょうど2セントです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.