カッコウで「濡れた」オブジェクト







この投稿は、Godfrey Nolanによる記事「 Mocking in Swift with Cuckoo」に基づいています







モバイル開発者による彼の「サービス」の義務のため、私の前にタスクが現れました:単体テストのためのMokovの作成と使用に対処することです。 私の同僚はカッコウの図書館を勧めました。 私は彼女に対処し始めました、それはそれから来たものです。







ドキュメント



githubのドキュメントを読んだ後、残念ながら、プロジェクトでCuckooを「取得」できませんでした。 このフレームワークはCocoaPodsを介してインストールされましたが、Runスクリプトに問題がありました。提案された例では、テストフォルダーにGeneratedMocks.swift



ファイルが作成されず、 。







そのため、すべての段階を一緒に進め、いくつかのニュアンスに対処します。







テストプロジェクト



当然、Cuckooをプラグインしてテストを作成するプロジェクトが必要です。 Xcodeを開き、新しいシングルビューアプリケーションを作成します。言語はSwiftです。必ず、 Include Unit Tests



チェックボックスをUrlWithCuckoo



、プロジェクト名はUrlWithCuckoo



です。







新しいSwiftファイルをプロジェクトに追加し、 UrlSession.swift



という名前をUrlSession.swift



ます。 完全なコードは次のとおりです。







 import Foundation class UrlSession { var url:URL? var session:URLSession? var apiUrl:String? func getSourceUrl(apiUrl:String) -> URL { url = URL(string:apiUrl) return url! } func callApi(url:URL) -> String { session = URLSession() var outputdata:String = "" let task = session?.dataTask(with: url as URL) { (data, _, _) -> Void in if let data = data { outputdata = String(data: data, encoding: String.Encoding.utf8)! print(outputdata) } } task?.resume() return outputdata } }
      
      





ご覧のとおり、これは3つのプロパティと2つのメソッドを持つ単純なクラスです。 このクラスのためにモックを作成します。







カッコウを接続する



私は仕事でCocoaPodsを使用しているため、Cuckooに接続するには、このタイプのディレクトリをPodfileプロジェクトディレクトリに追加します。







 platform :ios, '9.0' use_frameworks! target 'UrlWithCuckooTests' do pod 'Cuckoo' end
      
      





当然、プロジェクトディレクトリからターミナルでpod install



を実行する必要があります。インストールが完了しUrlWithCuckoo.xcworkspace



、XcodeでUrlWithCuckoo.xcworkspace



を開きます。







次のステップは、「ターゲット」テストのビルドフェーズに実行スクリプトを追加することです(「+」をクリックして「新規実行スクリプトフェーズ」を選択する必要があります)。













完全なスクリプトは次のとおりです。







 # Define output file; change "${PROJECT_NAME}Tests" to your test's root source folder, if it's not the default name OUTPUT_FILE="./${PROJECT_NAME}Tests/GeneratedMocks.swift" echo "Generated Mocks File = ${OUTPUT_FILE}" # Define input directory; change "${PROJECT_NAME}" to your project's root source folder, if it's not the default name INPUT_DIR="./${PROJECT_NAME}" echo "Mocks Input Directory = ${INPUT_DIR}" # Generate mock files; include as many input files as you'd like to create mocks for ${PODS_ROOT}/Cuckoo/run generate --testable "${PROJECT_NAME}" \ --output "${OUTPUT_FILE}" \ "${INPUT_DIR}/UrlSession.swift"
      
      





ご覧のとおり、スクリプトのコメントには、 ${PROJECT_NAME}Tests



${PROJECT_NAME}Tests



を置き換える必要があることが記載されていますが、この例では必要ありません。







モックを生成(s)



次に、このスクリプトが動作し、テストディレクトリにGeneratedMocks.swift



ファイルを作成する必要があります。プロジェクト( Cmd+B



)をビルドするだけでは不十分です。 Build Shift+Cmd+U



> Testing( Shift+Cmd+U



)を実行する必要があります:













GeneratedMocks.swift



ファイルがUrlWithCuckooTests



ディレクトリに表示されることGeneratedMocks.swift



確認します。 その(ファイル)もプロジェクト自体に追加する必要があります:FinderからUrlWithCuckooTests



XcodeにドラッグするだけUrlWithCuckooTests















モキの準備ができました。いくつかのニュアンスについて話しましょう。







1.複雑なファイル構造



プロジェクトに通常のファイル構造があり、ファイルがルートディレクトリだけでなくサブフォルダに配置されている場合は、スクリプトを調整する必要があります。







プロジェクトでMVPを使用し、 MainModule



モジュールのMainModule



Controllerのモックが必要であるとMainModule



ます(もちろん、プロジェクトでは/Modules/MainModule/MainModuleViewController.swift



)。 この場合、スクリプトの最後の行を"${INPUT_DIR}/UrlSession.swift"



例から"${INPUT_DIR}/UrlSession.swift"



に変更する必要があります。







また、 GeneratedMocks.swift



ファイルをテストのルートディレクトリだけでなく、たとえばModules



サブフォルダーにもOUTPUT_FILE="./${PROJECT_NAME}Tests/GeneratedMocks.swift"



たい場合は、スクリプトの次の行を修正する必要がありますOUTPUT_FILE="./${PROJECT_NAME}Tests/GeneratedMocks.swift"









2. Mokiのいくつかのクラスが必要



いくつかのクラスのMokiが必要になる可能性は非常に高いです(予想される確率は99.9%です)。 スクリプトの最後にMokiを作成する必要のあるファイルをリストし、バックスラッシュで分割するだけで作成できます。







 "${INPUT_DIR}/UrlSession.swift" \ "${INPUT_DIR}/Modules/MainModule/MainModuleViewController.swift" \ "${INPUT_DIR}/MyAwesomeObject.swift"
      
      





3.型注釈



Mokiを作成するクラスでは、すべてのプロパティに型注釈が必要です。 このようなものがある場合:







 var someBoolVariable = false
      
      





その後、Mokを生成すると、エラーが発生します。













ファイルGeneratedMocks.swift



__UnknownType



が表示され__UnknownType















残念ながら、Cuckooはデフォルト値でタイプを決定できません。この場合、プロパティのタイプを明示的に指定する必要があります。







 var someBoolVariable: Bool = false
      
      





テストを書く



次に、モックを使用して簡単なテストを作成しましょう。 UrlWithCuckooTests.swift



ファイルを開き、デフォルトで作成される2つのメソッドfunc testExample()



およびfunc testPerformanceExample()



ます。 それらは必要ありません。 そしてもちろん、忘れないでください:







 import Cuckoo
      
      





1.プロパティ



最初に、プロパティのテストを記述します。 新しいメソッドを作成します。







 func testVariables() { }
      
      





その中のMockといくつかの追加の定数を初期化します。







 let mock = MockUrlSession() let urlStr = "http://habrahabr.ru" let url = URL(string:urlStr)!
      
      





次に、プロパティのスタブを記述する必要があります。







 // Arrange stub(mock) { (mock) in when(mock.url).get.thenReturn(url) } stub(mock) { (mock) in when(mock.session).get.thenReturn(URLSession()) } stub(mock) { (mock) in when(mock.apiUrl).get.thenReturn(urlStr) }
      
      





スタブは、返される結果を置き換えるようなものです。 大ざっぱに言えば、我々が真岡に頼ったときに真岡の財産を返すものを説明します。 ご覧のthenReturn



thenReturn



を使用していますが、 thenReturn



を使用できます。 これは、値を返すだけでなく、追加のアクションを実行する機会を提供します。 たとえば、最初のスタブは次のように記述できます。







 // Arrange stub(mock) { (mock) in when(mock.url).get.then { (_) -> URL? in // some actions here return url } }
      
      





そして、実際には、(値とnil



)チェックします:







 // Act and Assert XCTAssertEqual(mock.url?.absoluteString, urlStr) XCTAssertNotNil(mock.session) XCTAssertEqual(mock.apiUrl, urlStr) XCTAssertNotNil(verify(mock).url) XCTAssertNotNil(verify(mock).session) XCTAssertNotNil(verify(mock).apiUrl)
      
      





2.メソッド



次に、Mokaのメソッドの呼び出しをテストします。 2つのテストメソッドを作成してみましょう。







 func testGetSourceUrl() { } func testCallApi() { }
      
      





どちらの方法でも、モック定数と補助定数を初期化します。







 let mock = MockUrlSession() let urlStr = "http://habrahabr.ru" let url = URL(string:urlStr)!
      
      





また、 testCallApi()



メソッドで、呼び出しカウンターを追加します。







 var callApiCount = 0
      
      





さらに両方のメソッドで、スタブを作成します。







testGetSourceUrl()









 // Arrange stub(mock) { (mock) in mock.getSourceUrl(apiUrl: urlStr).thenReturn(url) }
      
      





testCallApi()









 // Arrange stub(mock) { mock in mock.callApi(url: equal(to: url, equalWhen: { $0 == $1 })).then { (_) -> String in callApiCount += 1 return "{'firstName': 'John','lastName': 'Smith'}" } }
      
      





最初の方法を確認します。







 // Act and Assert XCTAssertEqual(mock.getSourceUrl(apiUrl: urlStr), url) XCTAssertNotEqual(mock.getSourceUrl(apiUrl: urlStr), URL(string:"http://google.com")) verify(mock, times(2)).getSourceUrl(apiUrl: urlStr)
      
      





(最後の行では、メソッドが2回呼び出されたことを確認します)







そして2つ目:







 // Act and Assert XCTAssertEqual(mock.callApi(url: url),"{'firstName': 'John','lastName': 'Smith'}") XCTAssertNotEqual(mock.callApi(url: url), "Something else") verify(mock, times(2)).callApi(url: equal(to: url, equalWhen: { $0 == $1 })) XCTAssertEqual(callApiCount, 2)
      
      





(ここで、2つの方法で呼び出しの数も確認します:以前に発表したverify



callApiCount



呼び出しcallApiCount



を使用します)







テストを実行する



テスト用のプロジェクト( Cmd+U



)を開始すると、次の図が表示されます。













すべてがうまくいきます。 :)







そして最後に



最終結果へのリンク: https : //github.com/ssuhanov/UrlWithCuckoo







ご清聴ありがとうございました。








All Articles