.. Unit Testing Guidelines .. _testing_guidelines: 単体テストガイドライン ======================= .. The Pylons Project rather rigorously follows a unit testing dogma along the .. lines described by Tres Seaver in `Avoiding Temptation: Notes on using .. unittest effectively .. `_ .. which this document is based on. Pylons プロジェクトは、このドキュメントの元となった Tres Seaver によって書かれた `Avoiding Temptation: Notes on using unittest effectively `_ に倣ったユニットテスト教義をかなり厳格に守っています。 .. note:: .. This document deals almost exclusively with *unit* tests. We have no .. particular dogma for integration tests or functional tests, although many .. of the tips below can be reused in that context. このドキュメントは、ほとんど排他的に *単体* テストを扱います。 結合テストまたは機能テストに対しては私たちは特別の教義を持っていません。 しかし、下記の tips の多くはその文脈でも再利用できます。 .. Tips for Avoiding Bad Unit Tests 悪い単体テストを避けるための tips --------------------------------- .. * Some folks have drunk the "don't repeat yourself" KoolAid: we agree that not .. repeating code is a virtue in most cases, but unit test code is an exception: .. cleverness in a test both obscures the intent of the test and makes a .. subsequent failure massively harder to diagnose. * ある人々は "don't repeat yourself" をむやみに信じてきました (drink the KoolAid): 私たちは、ほとんどの場合にはコードの繰り返しをしないことが 美徳であることに同意します。しかし、単体テストコードはその例外です: テストでの賢さは、テストの意図を不明瞭にし、後に起こる失敗の分析を 極めて困難にします。 .. * Others want to avoid writing both tests and documentation: they try to write .. test cases (almost invariably as "doctests") which do the work of real tests, .. while at the same time trying to make "readable" docs. * 別の人々は、テストとドキュメンテーションの両方を書かないようにしたいと 望んでいます: 彼らは実際のテストの仕事をするテストケースを書こうとする (ほとんど常に "doctest" として) 一方で、それと同時に「読みやすい」 ドキュメントを作ろうとします。 .. Most of the issues involved with the first motive are satisfactorily addressed .. later in this document: refusing to share code between test modules makes most .. temptations to cleverness go away. Where the temptation remains, the cure is to .. look at an individual test and ask the following questions: 最初の動機に関連したほとんどの問題には、このドキュメントの後で十分に 取り組みます: テストモジュール間のコードの共有を拒絶することは、ほとんど の賢さへの誘惑をなくします。残りの誘惑に対して、その対策は個別のテストを 見て次の質問をすることです: .. * Is the intent of the test clearly explained by the name of the testcase? * テストの意図は、テストケースの名前によって明確に説明されていますか? .. * Does the test follow the "canonical" form for a unit test? I.e., does it: .. * set up the preconditions for the method / function being tested. .. * call the method / function exactly one time, passing in the values .. established in the first step. .. * make assertions about the return value, and / or any side effects. .. * do absolutely nothing else. * そのテストは、単体テストのための「規範的な」形式に従っていますか? つまり: * テスト対象のメソッド / 関数のための事前条件をセットアップする * 最初のステップで確立された値を渡して、メソッド / 関数を一度だけ呼ぶ * 戻り値と任意の副作用に関するアサーションを行う * それ以外には何も行わない .. Fixing tests which fail along the "don't repeat yourself" axis is usually .. straightforward: "don't repeat yourself" の軸に沿って失敗するテストの修正は、通常は直裁的 (straightforward) です: .. * Replace any "generic" setup code with per-test-case code. The classic case .. here is code in the setUp method which stores values on the self of the test .. case class: such code is always capable of refactoring to use helper methods .. which return the appropriately-configured test objects on a per-test basis. * あらゆる「汎用的な」セットアップコードを、テストケースごとのコードに 置き換える。ここで典型的なケースとして setUp メソッドの中でテストケース クラスの self に値を格納するコードがあります: そのようなコードに対しては、 テスト単位で適切に設定されたテストオブジェクトを返すヘルパーメソッドを 使用するリファクタリングがいつでも行えます。 .. * If the method / function under test is called more than once, clone (and .. rename appropriately) the test case method, removing any redundant setup / .. assertions, until each test case calls it exactly once. * テスト対象のメソッド / 関数が二度以上呼ばれる場合は、テストケースメソッドを 複製して (そして適切に改名して)、 すべての冗長な setup / アサーションを 削除する。これを、それぞれのテストケースがテスト対象を一度だけ呼ぶように なるまで続けます。 .. Rewriting tests to conform to this pattern has a number of benefits: このパターンに一致するようにテストを書き直すことには多くの利点があります: .. * Each individual test case specifies exactly one code path through the method / .. function being tested, which means that achieving "100% coverage" means you .. really did test it all. * 個々のテストケースは、テスト対象のメソッド / 関数を通るただ1つのコードパス を特定します。これは、「100% カバレージ」の達成が、実際にそれをすべて テストしたことを意味するということです。 .. * The set of test cases for the method / function being tested define the .. contract very clearly: any ambiguity can be solved by adding one or more .. additional tests. * テスト対象のメソッド / 関数のためのテストケースのセットは、きわめて明確に 契約 (contract) を定義します: 追加のテストを加えることで、どんな曖昧さも 解消することができます。 .. * Any test which fails is going to be easier to diagnose, because the .. combination of its name, its preconditions, and its expected results are going .. to be clearly focused. * 失敗したテストの分析がより簡単になるでしょう。なぜなら、その名前、 事前条件、予期される結果の組み合わせがはっきりと集中しているからです。 .. Goals ゴール ------ .. The goals of the kind of testing outlined here are simplicity, loose or no .. coupling, and speed: ここで概説されているテストのゴールは、単純さ、疎結合 (あるいは結合がないこと)、 速度です: .. * Tests should be as simple as possible, while exercising the application- .. under-test (AUT) completely. .. * Tests should run as quickly as possible, to encourage running them .. frequently. .. * Tests should avoid coupling with other tests, or with parts of the AUT which .. they are not responsible for testing. * テスト対象のアプリケーション (application-under-test; AUT) をそのまま使う 一方で、テストはできるだけ単純であるべきです。 * テストを頻繁に実行することを促進するため、可能な限り素早く動作すべきです。 * テストは他のテストあるいはテストに関係ない AUT の一部との密結合を 避けるべきです。 .. Developers write such tests to verify that the AUT is abiding by the contracts .. the developer specifies. While an instance this type of test case may be .. illustrative of the contract it tests, such test cases do not take the place .. of either API documentation or of narrative / "theory of operations" .. documentation. Still less are they intended for end-user documentation. 開発者によって指定された契約によって AUT が不変であることを確認するため、 開発者はこのようなテストを書きます。この種のテストケースは、例えそれが テストしようとする契約の説明に役立つとしても、 API ドキュメンテーション、 あるいは narrative /「動作に関する理論」ドキュメンテーションの代わりに はなりません。また、それらはもちろんエンドユーザドキュメンテーションを 意図したものでもありません。 .. Rule: Avoid doctests ルール: doctest を避ける ------------------------ .. Doctests seem to fulfill the best of both worlds, providing documentation .. *and* testing. In reality, tests written using doctest almost always serve as .. both poor tests and poor documentation. doctest はドキュメンテーションとテストの *両方* を提供して、一石二鳥を 達成するように思えます。実際には、 doctest を使用して書かれたテストは、 ほとんど常に貧弱なテストと貧弱なドキュメンテーションにしかなりません。 .. - Good tests often need to test obscure edge cases, and tests for obscure .. edge cases don't make particularly good reading as documentation. - よいテストはしばしば曖昧なエッジケースをテストする必要があります。 そして、曖昧なエッジケースに対するテストはドキュメンテーションとして 特に読んで面白いものではありません。 .. - Doctests are harder to debug than "normal" unit tests. It's easy to "pdb" .. step through a normal unit test, it's much harder to do so for doctests. - doctest は「通常の」単体テストよりデバッグするのがより困難です。 通常の単体テストであれば簡単に "pdb" でステップ実行することができますが、 doctest に対してそれを行うことははるかに困難です。 .. - Doctests expose too many implementation details of the interpreter (such as .. the representation format of a class when printed). Often doctests break .. when interpreter versions change, and the ameliorations that allow doctests .. to straddle representations between versions then cause the doctest to .. become ugly and fragile. - doctest は (出力された時のクラスの表現フォーマットのような) インタープリタの実装詳細を過度に露出します。インタープリタの バージョンを変更しただけでしばしば doctest は壊れ、バージョン間の 表現の違いを吸収するための改良が doctest を醜く脆弱にします。 .. - Doctests have an execution model that makes it difficult to follow many of .. the rest of the rules in this document. - doctest は、このドキュメント中の他の多くのルールに従うことを難しくする 実行モデルを持っています。 .. - Doctests often encourage bad testing practice (cutting an unverified .. outcome of a function call and pasting it into a test suite). - doctest はしばしば悪いテスト習慣 (関数呼び出しの結果を検証せずに切り取り、 テストスートにそれを貼り付ける) を促進します。 .. Rule: Never import the module-under-test at test module scope. ルール: テスト対象のモジュールをテストモジュールのスコープでインポートしない ---------------------------------------------------------------------------- .. Import failures in the module-under-test (MUT) should cause individual test .. cases to fail: they should never prevent those tests from being run. Depending .. on the testrunner, import problems may be much harder to distinguish at a .. glance than normal test failures. テスト対象のモジュール (module-under-test; MUT) でインポートに問題があると、 それぞれのテストケースは失敗します: インポートエラーによってテスト実行が 阻害されるべきではありません。テストランナーにもよりますが、 通常のテスト失敗に比べてインポートの問題は一目で識別することがはるかに 難しい場合があります。 .. For example, rather than the following: 例えば以下のようにするのではなく: .. code-block:: python :linenos: # test the foo module import unittest from package.foo import FooClass class FooClassTests(unittest.TestCase): def test_bar(self): foo = FooClass('Bar') self.assertEqual(foo.bar(), 'Bar') .. prefer: このようにしてください: .. code-block:: python :linenos: # test the foo module import unittest class FooClassTests(unittest.TestCase): def _getTargetClass(self): from package.foo import FooClass return FooClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_bar(self): foo = self._makeOne('Bar') self.assertEqual(foo.bar(), 'Bar') .. Guideline: Minimize module-scope dependencies. ガイドライン: モジュールスコープの依存性を最小化する ---------------------------------------------------- .. Unit tests need to be runnable even in an environment which is missing some .. required features: in that case, one or more of the testcase methods (TCMs) .. will fail. Defer imports of any needed library modules as late as possible. 単体テストは必要な機能の一部が存在しない環境でも実行できるようにする必要が あります: その場合、1つ以上のテストケースメソッド (TCM) は失敗するでしょう。 あらゆる必要なライブラリーモジュールのインポートをできるだけ遅らせてください。 .. For instance, this example generates no test failures at all if the ``qux`` .. module is not importable: 例えば、 ``qux`` モジュールがインポートできない場合、この例ではテストの 失敗がまったく発生しません: .. code-block:: python :linenos: # test the foo module import unittest import qux class FooClassTests(unittest.TestCase): def _getTargetClass(self): from package.foo import FooClass return FooClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_bar(self): foo = self._makeOne(qux.Qux('Bar')) .. while this example raises failures for each TCM which uses the missing .. module: 一方、この例は不足しているモジュールを使用する各 TCM の失敗を報告します: .. code-block:: python :linenos: # test the foo module import unittest class FooClassTests(unittest.TestCase): def _getTargetClass(self): from package.foo import FooClass return FooClass def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_bar(self): import qux foo = self._makeOne(qux.Qux('Bar')) .. It may be a reasonable tradeoff in some cases to import a module (but not the .. MUT!) which is used widely within the test cases. Such a tradeoff should .. probably occur late in the life of the TCM, after the pattern of usage is .. clearly understood. いくつかの場合では、テストケース内で広く使用されるモジュール (しかし MUT ではない!) をインポートすることは合理的なトレードオフかもしれません。 使用法のパターンを明白に理解した後で、おそらくそのようなトレードオフが TCM の寿命の中でやがて生じることがあるでしょう。 .. Rule: Make each test case method test Just One Thing. ルール: 各テストケースメソッドは、 1つのことだけをテストする ------------------------------------------------------------ .. Avoid the temptation to write fewer, bigger tests. Ideally, each TCM will .. exercise one set of preconditions for one method or function. For instance, .. the following test case tries to exercise far too much: 少数の大きなテストを書こうとする誘惑を避けてください。理想的には、それぞれの TCM は1つのメソッドあるいは関数の特定の事前条件の組み合わせに対してテストを 行います。例えば、次のテストケースはあまりにも多くをテストしようとしています: .. code-block:: python :linenos: def test_bound_used_container(self): from AccessControl.SecurityManagement import newSecurityManager from AccessControl import Unauthorized newSecurityManager(None, UnderprivilegedUser()) root = self._makeTree() guarded = root._getOb('guarded') ps = guarded._getOb('bound_used_container_ps') self.assertRaises(Unauthorized, ps) ps = guarded._getOb('container_str_ps') self.assertRaises(Unauthorized, ps) ps = guarded._getOb('container_ps') container = ps() self.assertRaises(Unauthorized, container) self.assertRaises(Unauthorized, container.index_html) try: str(container) except Unauthorized: pass else: self.fail("str(container) didn't raise Unauthorized!") ps = guarded._getOb('bound_used_container_ps') ps._proxy_roles = ( 'Manager', ) ps() ps = guarded._getOb('container_str_ps') ps._proxy_roles = ( 'Manager', ) ps() .. This test has a couple of faults, but the critical one is that it tests too .. many things (eight different cases). このテストはいくつかの欠点を持っています。しかし、最も致命的なのは、 あまりにも多くのこと (8つの異なるケース) をテストしているということです。 .. In general, the prolog of the TCM should establish the one set of .. preconditions by setting up fixtures / mock objects / static values, and then .. instantiate the class or import the FUT (function-under-test). The TCM should .. then call the method / function. The epilog should test the outcomes, .. typically by examining either the return value or the state of one or more .. fixtures / mock objects. 一般に TCM のプロローグは、 fixture / モックオブジェクト / 静的な値 のセットアップによって特定の事前条件の組み合わせを確立し、次にクラスを インスタンス化するかテスト対象の関数 (function-under-test; FUT) を インポートします。その後 TCM はメソッド / 関数を呼びます。エピローグは 結果をテストします。典型的には、戻り値あるいは1つ以上の fixture / モックオブジェクトの状態のいずれかの検査によって行われます。 .. Thinking about the separate sets of preconditions for each function or method .. being tested helps clarify the contract, and may inspire a simpler / cleaner / .. faster implementation. テスト対象の関数あるいはメソッドそれぞれに対する事前条件の組み合わせについて 考えることは、契約を明確にすることを助け、より単純な / クリーンな / より速い 実装の動機となるでしょう。 .. Rule: Name TCMs to indicate what they test. ルール: TCM にそれが何をテストしているかを示す名前を付ける ---------------------------------------------------------- .. The name of the test should be the first, most useful clue when looking at a .. failure report: don't make the reader (yourself, most likely) grep the test .. module to figure out what was being tested. テストの名前は失敗レポートを見る場合の最初にして最も有用な手掛かりと なるでしょう: 何がテストされていたかを考えるために、読者 (一番ありえるのは あなた自身です) にテストモジュールを grep させないでください。 .. Rather than adding a comment: コメントを追加する代わりに、 .. code-block:: python :linenos: class FooClassTests(unittest.TestCase): def test_some_random_blather(self): # test the 'bar' method in the case where 'baz' is not set. ... .. prefer to use the TCM name to indicate its purpose: テストの目的を示すために TCM 名を使用するようにしてください: .. code-block:: python :linenos: class FooClassTests(unittest.TestCase): def test_getBar_wo_baz(self): ... .. Guideline: Share setup via helper methods, not via attributes of ``self``. ガイドライン: ``self`` の属性によってではなく、ヘルパーメソッドによってセットアップを共有する --------------------------------------------------------------------------------------------- .. Doing unneeded work in the ``setUp`` method of a testcase class sharply .. increases coupling between TCMs, which is a Bad Thing. For instance, suppose .. the class-under-test (CUT) takes a context as an argument to its constructor. .. Rather than instantiating the context in ``setUp``: テストケースクラスの ``setUp`` メソッドで不要な仕事をすることは、 TCM 同士の結合性を急激に増加させます。それは悪いこと (Bad Thing) です。 例えば、テスト対象のクラス (class-under-test; CUT) がそのコンストラクタに 引数としてコンテキストを受け取ると仮定してください。 ``setUp`` の中で コンテキストをインスタンス化するのではなく: .. code-block:: python :linenos: class FooClassTests(unittest.TestCase): def setUp(self): self.context = DummyContext() # ... def test_bar(self): foo = self._makeOne(self.context) .. add a helper method to instantiate the context, and keep it as a local: コンテキストをインスタンス化するヘルパーメソッドを追加して、そのインスタンス をローカルに保持するようにしてください: .. code-block:: python :linenos: class FooClassTests(unittest.TestCase): def _makeContext(self, *args, **kw): return DummyContext(*args, **kw) def test_bar(self): context = self._makeContext() foo = self._makeOne(context) .. This practice allows different tests to create the mock context differently, .. avoiding coupling. It also makes the tests run faster, as the tests which .. don't need the context don't pay for creating it. このプラクティスは、結合性を回避しながら異なるテストが異なった風に モックのコンテキストを作成することを可能にします。さらに、コンテキストを 必要としないテストがコンテキスト作成の代価を払わないので、それはテストの 実行をより速くします。 .. Guideline: Make fixtures as simple as possible. ガイドライン: fixture を可能な限り単純にしてください ---------------------------------------------------- .. When writing a mock object, start off with an empty class, e.g.: モックオブジェクトを書く場合、空のクラスから初めてください。例えば: .. code-block:: python :linenos: class DummyContext: pass .. Run the tests, adding methods only enough to the mock object to make the .. dependent tests pass. Avoid giving the mock object any behavior which is not .. necessary to make one or more tests pass. テストを実行して、依存するテストを通過させるのに必要なメソッドだけを モックオブジェクトに加えてください。モックオブジェクトにテストを通過 させるのに必要ない振る舞いを与えないようにしてください。 .. Guideline: Use hooks and registries judiciously. フックとレジストリを賢く使ってください ------------------------------------------------ .. If the application already allows registering plugins or components, take .. advantage of the fact to insert your mock objects. Don't forget to clean up .. after each test! アプリケーションが既にプラグインまたはコンポーネントを登録できるように なっている場合は、モックオブジェクトを挿入するためにその事実を利用して ください。各テストの後でクリーンアップを忘れないでください! .. It may be acceptable to add hook methods to the application, purely to allow .. for simplicity of testing. For instance, code which normally sets datetime .. attributes to "now" could be tweaked to use a module-scope function, rather .. than calling ``datetime.now()`` directly. Tests can then replace that function .. with one which returns a known value (as long as they put back the original .. version after they run). アプリケーションに純粋にテストの単純性を考慮したフックメソッドを追加 することも許容可能です。例えば、通常は datetime 属性を「今」にセットする コードで、 ``datetime.now()`` を直接呼ぶのではなく、モジュールスコープの 関数を使用するように調整を加えることができます。その後、テストはその 関数を既知の値を返す関数に置き替えることができます (ただし、テストが実行 された後でオリジナル版に戻す必要があります)。 .. Guideline: Use mock objects to clarify dependent contracts ガイドライン: 依存的契約を明確化するために、モックオブジェクトを使用してください -------------------------------------------------------------------------------- .. Keeping the contracts on which the AUT depends as simple as possible makes .. the AUT easier to write, and more resilient to changes. Writing mock objects .. which supply only the simplest possible implementation of such contracts keeps .. the AUT from acquiring "dependency creep." AUT が依存する契約を可能な限り単純に維持することは、 AUT を書きやすくして、 また変更に対する弾力性を高めます。そのような契約の可能な限り単純な実装 だけを提供するモックオブジェクトを書くことで、AUT が「依存性の劣化 (dependency creep)」を起こさないようにします。 .. For example, in a relational application, the SQL queries used by the .. application can be mocked up as a dummy implementation which takes keyword .. parameters and returns lists of dictionaries: 例えば、リレーショナルアプリケーションでは、アプリケーションによって 使用される SQL クエリは、キーワードパラメータを取って辞書のリストを返す ダミー実装としてモック化することができます: .. code-block:: python :linenos: class DummySQL: def __init__(self, results): # results should be a list of lists of dictionaries self.called_with = [] self.results = results def __call__(self, **kw): self.called_with.append(kw.copy()) return results.pop(0) .. In addition to keeping the dependent contract simple (in this case, the SQL .. object should return a list of mappings, one per row), the mock object allows .. for easy testing of how it is used by the AUT: 依存している契約を単純に保つ (この場合、 SQL オブジェクトが列当たり一つの マッピングをリストにして返す) ことに加えて、モックオブジェクトは、 それが AUT によってどのように使われるかを簡単にテストできるようにします: .. code-block:: python :linenos: class FooTest(unittest.TestCase): def test_barflies_returns_names_from_SQL(self): from foo.sqlregistry import registerSQL RESULTS = [[{'name': 'Chuck', 'drink': 'Guiness'}, {'name': 'Bob', 'drink': 'Knob Creek'}, ]] query = DummySQL(RESULTS[:]) registerSQL('list_barflies', query) foo = self._makeOne('Dog and Whistle') names = foo.barflies() self.assertEqual(len(names), len(RESULTS)) self.failUnless('NAME1' in names) self.failUnless('NAME2' in names) self.assertEqual(query.called_with, [{'bar': 'Dog and Whistle'}]) .. Rule: Don't share text fixtures between test modules. ルール: テストモジュール間で fixture を共有しない ----------------------------------------------------- .. The temptation here is to save typing by borrowing mock objects or fixture .. code from another test module. Once indulged, one often ends up moving such .. "generic" fixtures to shared modules. ここでの誘惑は、モックオブジェクトや fixture コードを別のテストモジュール から借りてくることでタイプ数を節約しようとすることです。一旦そのようなことを 許してしまうと、やがてそのような「汎用的な」 fixture を共有モジュールに 移動しようとする人が現れます。 .. The rationale for this prohibition is simplicity: unit tests need to exercise .. the AUT, while remaining as clear and simple as possible. これを禁止する根拠は、単純性です: 単体テストでは、 AUT をできるだけ明白で 単純なまま使うことが必要です。 .. * Because they are not in the module which uses them, shared mock objects and .. fixtures imposes a lookup burden on the reader. * それらが同じモジュールの中にないため。共有のモックオブジェクトや fixture は読者に検索の負担を課します。 .. * Because they have to support APIs used by multiple clients, shared fixtures .. tend to grow APIs / data structures needed only by one client: in the .. degenerate case, become as complicated as the application they are supposed .. to stand in for! * それらが多数のクライアントによって使用される API をサポートしなければ ならないため。共有の fixture は1つのクライアントによってのみ必要と される API / データ構造を拡大する傾向があります: ひどい場合には、 置き換えようとするアプリケーションと同じくらい複雑になります! .. In some cases, it may be cleaner to avoid sharing fixtures even among test .. case methods (TCMs) within the same module / class. 場合によっては、同じモジュール / クラス内のテストケースメソッド (TCM) の 中でさえ fixture を共有しないようにするほうがクリーンかもしれません。 .. Conclusion 結論 ---------- .. Tests which conform to these rules and guidelines have the following properties: これらのルールとガイドラインに従うテストは次の特性を持ちます: .. * The tests are straightforward to write. .. * The tests yield excellent coverage of the AUT. .. * They reward the developer through predictable feedback (e.g., the growing .. list of dots for passed tests). .. * They run quickly, and thus encourage the developer to run them frequently. .. * Expected failures confirm missing / incomplete implementations. .. * Unexpected failures are easy to diagnose and repair. .. * When used as regression tests, failures help pinpoint the exact source of .. the regression (a changed contract, for instance, or an underspecified .. constraint). .. * Writing such tests clarifies thinking about the contracts of the code they .. test, as well as the dependencies of that code. * そのようなテストは直裁的に書くことができます。 * そのようなテストは AUT の優れたカバレージを生みます。 * 予測可能なフィードバック (例えば通過したテストに対してドットのリストが 増え続けるなど) によって、開発者にもメリットがあります。 * テストが素早く実行され、そのためテストを頻繁に行うように開発者を促します。 * 予期された失敗は、不足している / 不完全な実装を確認します。 * 予期しない失敗は、簡単に分析して修正することができます。 * 退行テストとして使用された場合、失敗は退行の正確な原因を知る助けになります (例えば変更された契約、あるいは指定されていない制約)。 * そのようなテストを書くことは、テストしているコードの契約に加えてその コードの依存性に関する思考を明確化します。