
この投稿は、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
ご清聴ありがとうございました。