コードをテスト可能にする依存インジェクションを使用します


あなたのコードのユニットテストを書きたいことがありますが、あなたはそれを行うのは難しいことを発見した?多くの場合、これはテストでコードを記述しない結果です.これを解決する簡単な方法はtest-driven development , あなたのアプリケーションコードの前にテストを書く開発プロセス.
しかし、テスト駆動開発のファンでない場合でも、簡単なテクニック、依存性注入を使用してコードをテストすることができます.

依存注入は何ですか?


依存性注入は非常に簡単ですが、信じられないほど強力なテクニックです.つまり、依存関係をハードコードにコード化した関数ではなく、関数を使用して開発者が引数を通して必要な依存関係を通過させることができます.
コンセプトを固めるために、例を一緒に見ましょう.

クッキー文字列の解析



ジョン・ドーシーによる写真
個々のクッキーキー値ペアを解析できるJavaScript関数を書きたいとしましょうdocument.cookie 文字列.
たとえば、クッキーがあるかどうかチェックしたいと言うenable_cool_feature , 値がtrue , 次に、そのユーザーのサイトを参照していくつかのクールな機能を有効にします.
残念ながらdocument.cookie 文字列はJavaScriptで動作するように絶対にひどいです.我々がちょうど何かで資産価値を調べることができるならば、それは親切ですdocument.cookie.enable_cool_feature , しかし、ああ、我々はできません.
したがって、いくつかの潜在的に複雑な基礎的なコードの上に単純なファサードを提供する我々自身のクッキー解析機能を書くことに頼ります.
(レコード用には、JavaScriptライブラリとパッケージがいくつかありますので、必要に応じて独自のアプリケーションでこの関数を再書き込みする必要はありません.
最初のパスとして、以下のように定義された単純な関数が欲しいかもしれません.
function getCookie(cookieName) { /* body here */ }
この関数は、以下のように呼び出して特定のクッキーの値を見つけることができます.
getCookie('enable_cool_feature')

試料溶液



Unsplashによる写真
“JavaScriptのクッキー文字列を解析する方法”のGoogle検索は様々な開発者から多くの異なる解決策を明らかにする.この記事のために、我々は提供される解決を見ますW3Schools . 次のようになります.
export function getCookie(cookieName) {
  var name = cookieName + '='
  var decodedCookie = decodeURIComponent(document.cookie)
  var ca = decodedCookie.split(';')

  for (var i = 0; i < ca.length; i++) {
    var c = ca[i]
    while (c.charAt(0) == ' ') {
      c = c.substring(1)
    }

    if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length)
    }
  }

  return ''
}

サンプル溶液の批評



Hunyong華による写真
さて、これで何が悪いのか.コード自体の本体を批判しませんが、このコードの1行を見てみましょう.
var decodedCookie = decodeURIComponent(document.cookie)
機能getCookie に依存するdocument オブジェクトとcookie プロパティ!これは最初は大したことではないかもしれませんが、いくつかの欠点があります.
まず最初に、もしどんな理由でコードがアクセスできなかったのかdocument オブジェクト?例えば、ノード環境でdocument is undefined . これを説明するためのサンプルテストコードを見てみましょう.
Jestをテストフレームワークとして使い、2つのテストを書きましょう.
import { getCookie } from './get-cookie-bad'

describe('getCookie - Bad', () => {
  it('can correctly parse a cookie value for an existing cookie', () => {
    document.cookie = 'key2=value2'
    expect(getCookie('key2')).toEqual('value2')
  })

  it('can correctly parse a cookie value for a nonexistent cookie', () => {
    expect(getCookie('bad_key')).toEqual('')
  })
})
では、テストを実行して出力を見ましょう.
ReferenceError: document is not defined
ああ!ノード環境でdocument が定義されていない.幸運にも、我々は我々のJEST Configを変えることができますjest.config.js ファイルを指定しますjsdom , そして、それは私たちのテストで使用するためにDOMを作成します.
module.exports = {
  testEnvironment: 'jsdom'
}
我々が再び我々のテストを走らせるならば、彼らは通ります.しかし、我々はまだ少しの問題があります.私たちはdocument.cookie グローバルに文字列を返します.テストが異なる順序で動くならば、これは若干の変わったテストケースのために作ることができます.
例えば、もし書くならconsole.log(document.cookie) つ目のテストでは、まだ出力されますkey2=value2 . ああ!それは私たちが望むものではない.最初のテストは2回目のテストに影響します.この場合、2番目のテストはまだ通過しますが、あなたがお互いから分離されていないテストをするとき、若干の混乱している状況に入るのは非常に可能です.
これを解決するために、我々は最初のテストの後にクリーンアップのビットを行うことができますexpect
it('can correctly parse a cookie value for an existing cookie', () => {
  document.cookie = 'key2=value2'
  expect(getCookie('key2')).toEqual('value2')
  document.cookie = 'key2=; expires = Thu, 01 Jan 1970 00:00:00 GMT'
})
(一般的に、私はあなたにafterEach 各テストの後にコードを実行するメソッドです.しかし、クッキーを削除するだけで言うように簡単ではありませんdocument.cookie = '' あいにく
あなたが現在設定されていないクッキー文字列をパースしたいなら、w 3 schoolの解決策の2番目の問題は、それ自体を示しますdocument.cookie プロパティ.どうやってそんなことするの?この場合、あなたはできません!

より良い方法があります



Unsplashのカムアダムズによる写真
つの可能な解決策と2つの問題を調査したので、このメソッドを書くより良い方法を見てみましょう.依存インジェクションを使用します!
我々の機能署名は、我々の最初の解決と少し異なります.今回は二つの引数を受け付けます:
function getCookie(cookieString, cookieName) { /* body here */ }
以下のように呼びます.
getCookie(<someCookieStringHere>, 'enable_cool_feature')
サンプル実装は次のようになります.
export function getCookie(cookieString, cookieName) {
  var name = cookieName + '='
  var decodedCookie = decodeURIComponent(cookieString)
  var ca = decodedCookie.split(';')

  for (var i = 0; i < ca.length; i++) {
    var c = ca[i]
    while (c.charAt(0) == ' ') {
      c = c.substring(1)
    }

    if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length)
    }
  }

  return ''
}
この関数と元の関数の唯一の違いは、関数が2つの引数を受理し、それがcookieString 3行目でクッキーをデコードするとき.
では、この関数の2つのテストを書きましょう.これら2つのテストは、オリジナルの2つのテストが同じことをテストします.
import { getCookie } from './get-cookie-good'

describe('getCookie - Good', () => {
  it('can correctly parse a cookie value for an existing cookie', () => {
    const cookieString = 'key1=value1;key2=value2;key3=value3'
    const cookieName = 'key2'
    expect(getCookie(cookieString, cookieName)).toEqual('value2')
  })

  it('can correctly parse a cookie value for a nonexistent cookie', () => {
    const cookieString = 'key1=value1;key2=value2;key3=value3'
    const cookieName = 'bad_key'
    expect(getCookie(cookieString, cookieName)).toEqual('')
  })
})
我々のメソッドが現在使用しているクッキー文字列を完全に制御できる方法に注意してください.
我々は環境に頼る必要がない、我々はテストのハングアップに実行されません、我々は常に我々は常にクッキーを直接解析していると仮定する必要はありませんdocument.cookie .
より良い!

結論


それだ!依存性の注入は信じられないほど実装することは簡単です、そしてそれはあなたのテストを書くことを容易にすることによってあなたのテスト経験を大いに改善します、そして、あなたの依存関係はモックに簡単です.(もちろん、あなたのコードを分離するのを手伝っているわけではありませんが、それは別の日のトピックです).
読書ありがとう!