MockerはSwiftで書かれたライブラリで、カスタムURLProtocol
使用してデータ要求をモックすることができます。
すべてのデータリクエストユニットテストをオフラインで実行しますか?
URLSession
で動作しますAlamofire
などの一般的なフレームワークをサポートしていますユニットテストは、 Mocker
向けに書かれており、それがどのように機能するかを確認するのに役立ちます。
最初のMock
を登録した後、モッカーはURLSession.shared
のようなデフォルトのURLロードシステムに対して自動的にアクティブになります。
カスタムURLSession
で動作させるには、 MockingURLProtocol
登録する必要があります。
let configuration = URLSessionConfiguration . default
configuration . protocolClasses = [ MockingURLProtocol . self ]
let urlSession = URLSession ( configuration : configuration )
カスタムURLSession
に登録するのと非常に似ています。
let configuration = URLSessionConfiguration . af . default
configuration . protocolClasses = [ MockingURLProtocol . self ]
let sessionManager = Alamofire . Session ( configuration : configuration )
モックされたすべてのデータがアクセス可能なクラスを作成することをお勧めします。この例は、このプロジェクトの単体テストにあります。
public final class MockedData {
public static let botAvatarImageResponseHead : Data = try ! Data ( contentsOf : Bundle ( for : MockedData . self ) . url ( forResource : " Resources/Responses/bot-avatar-image-head " , withExtension : " data " ) ! )
public static let botAvatarImageFileUrl : URL = Bundle ( for : MockedData . self ) . url ( forResource : " wetransfer_bot_avater " , withExtension : " png " ) !
public static let exampleJSON : URL = Bundle ( for : MockedData . self ) . url ( forResource : " Resources/JSON Files/example " , withExtension : " json " ) !
}
let originalURL = URL ( string : " https://www.wetransfer.com/example.json " ) !
let mock = Mock ( url : originalURL , contentType : . json , statusCode : 200 , data : [
. get : try ! Data ( contentsOf : MockedData . exampleJSON ) // Data containing the JSON response
] )
mock . register ( )
URLSession . shared . dataTask ( with : originalURL ) { ( data , response , error ) in
guard let data = data , let jsonDictionary = ( try ? JSONSerialization . jsonObject ( with : data , options : [ ] ) ) as? [ String : Any ] else {
return
}
// jsonDictionary contains your JSON sample file data
// ..
} . resume ( )
let originalURL = URL ( string : " https://www.wetransfer.com/api/foobar " ) !
var request = URLRequest ( url : originalURL )
request . httpMethod = " PUT "
let mock = Mock ( request : request , statusCode : 204 )
mock . register ( )
URLSession . shared . dataTask ( with : originalURL ) { ( data , response , error ) in
// ....
} . resume ( )
認証URLのような一部のURLには、クエリにタイムスタンプまたはUUIDが含まれています。これらをock笑するには、特定のURLのクエリを無視できます。
/// Would transform to "https://www.example.com/api/authentication" for example.
let originalURL = URL ( string : " https://www.example.com/api/authentication?oauth_timestamp=151817037 " ) !
let mock = Mock ( url : originalURL , ignoreQuery : true , contentType : . json , statusCode : 200 , data : [
. get : try ! Data ( contentsOf : MockedData . exampleJSON ) // Data containing the JSON response
] )
mock . register ( )
URLSession . shared . dataTask ( with : originalURL ) { ( data , response , error ) in
guard let data = data , let jsonDictionary = ( try ? JSONSerialization . jsonObject ( with : data , options : [ ] ) ) as? [ String : Any ] else {
return
}
// jsonDictionary contains your JSON sample file data
// ..
} . resume ( )
let imageURL = URL ( string : " https://www.wetransfer.com/sample-image.png " ) !
Mock ( fileExtensions : " png " , contentType : . imagePNG , statusCode : 200 , data : [
. get : try ! Data ( contentsOf : MockedData . botAvatarImageFileUrl )
] ) . register ( )
URLSession . shared . dataTask ( with : imageURL ) { ( data , response , error ) in
let botAvatarImage : UIImage = UIImage ( data : data! ) ! // This is the image from your resources.
} . resume ( )
let exampleURL = URL ( string : " https://www.wetransfer.com/api/endpoint " ) !
Mock ( url : exampleURL , contentType : . json , statusCode : 200 , data : [
. head : try ! Data ( contentsOf : MockedData . headResponse ) ,
. get : try ! Data ( contentsOf : MockedData . exampleJSON )
] ) . register ( )
URLSession . shared . dataTask ( with : exampleURL ) { ( data , response , error ) in
// data is your mocked data
} . resume ( )
既に構築された静的DataType
実装に加えて、 Content-Type
ヘッダーキーの値として使用されるカスタムの実装を作成することができます。
let xmlURL = URL ( string : " https://www.wetransfer.com/sample-xml.xml " ) !
Mock ( fileExtensions : " png " , contentType : . init ( name : " xml " , headerValue : " text/xml " ) , statusCode : 200 , data : [
. get : try ! Data ( contentsOf : MockedData . sampleXML )
] ) . register ( )
URLSession . shared . dataTask ( with : xmlURL ) { ( data , response , error ) in
let sampleXML : Data = data // This is the xml from your resources.
} . resume (
リクエストのキャンセルが機能しているかどうかをテストしたい場合があります。その場合、mockedリクエストはすぐに終了せず、遅延が必要です。これは簡単に追加できます:
let exampleURL = URL ( string : " https://www.wetransfer.com/api/endpoint " ) !
var mock = Mock ( url : exampleURL , contentType : . json , statusCode : 200 , data : [
. head : try ! Data ( contentsOf : MockedData . headResponse ) ,
. get : try ! Data ( contentsOf : MockedData . exampleJSON )
] )
mock . delay = DispatchTimeInterval . seconds ( 5 )
mock . register ( )
時々、短いURLまたは他のリダイレクトURLをモックしたい場合があります。これは、応答を保存し、リダイレクトの場所をock笑することで可能です。これは、応答の内部にあります。
Date: Tue, 10 Oct 2017 07:28:33 GMT
Location: https://wetransfer.com/redirect
短いURLとリダイレクトURLのモックを作成することにより、リダイレクトを模倣してこの動作をテストできます。
let urlWhichRedirects : URL = URL ( string : " https://we.tl/redirect " ) !
Mock ( url : urlWhichRedirects , contentType : . html , statusCode : 200 , data : [ . get : try ! Data ( contentsOf : MockedData . redirectGET ) ] ) . register ( )
Mock ( url : URL ( string : " https://wetransfer.com/redirect " ) ! , contentType : . json , statusCode : 200 , data : [ . get : try ! Data ( contentsOf : MockedData . exampleJSON ) ] ) . register ( )
モッカーが登録時にデフォルトですべてのURLをキャッチすると、モックされたリクエストが必要ない場合に投げられるfatalError
になってしまう可能性があります。その場合、URLを無視できます。
let ignoredURL = URL ( string : " https://www.wetransfer.com " ) !
// Ignore any requests that exactly match the URL
Mocker . ignore ( ignoredURL )
// Ignore any requests that match the URL, with any query parameters
// e.g. https://www.wetransfer.com?foo=bar would be ignored
Mocker . ignore ( ignoredURL , matchType : . ignoreQuery )
// Ignore any requests that begin with the URL
// e.g. https://www.wetransfer.com/api/v1 would be ignored
Mocker . ignore ( ignoredURL , matchType : . prefix )
ただし、モックされたURLのみをキャッチし、他のすべてのURLを無視するためにモッカーが必要な場合は、 mode
属性を.optin
に設定できます。
Mocker . mode = . optin
元のモードを元に戻す場合は、 .optout
に設定するだけです。
Mocker . mode = . optout
Mock
を要求してエラーを返すことができ、エラー処理のテストを可能にします。
Mock ( url : originalURL , contentType : . json , statusCode : 500 , data : [ . get : Data ( ) ] ,
requestError : TestExampleError . example ) . register ( )
URLSession . shared . dataTask ( with : originalURL ) { ( data , urlresponse , err ) in
XCTAssertNil ( data )
XCTAssertNil ( urlresponse )
XCTAssertNotNil ( err )
if let err = err {
// there's not a particularly elegant way to verify an instance
// of an error, but this is a convenient workaround for testing
// purposes
XCTAssertEqual ( " example " , String ( describing : err ) )
}
expectation . fulfill ( )
} . resume ( )
Mock
コールバックに登録して、テストを容易にすることができます。
var mock = Mock ( url : request . url! , contentType : . json , statusCode : 200 , data : [ . post : Data ( ) ] )
mock . onRequestHandler = OnRequestHandler ( httpBodyType : [ [ String : String ] ] . self , callback : { request , postBodyArguments in
XCTAssertEqual ( request . url , mock . request . url )
XCTAssertEqual ( expectedParameters , postBodyArguments )
onRequestExpectation . fulfill ( )
} )
mock . completion = {
endpointIsCalledExpectation . fulfill ( )
}
mock . register ( )
completion
とonRequest
設定する代わりに、期待を利用することもできます。
var mock = Mock ( url : url , contentType : . json , statusCode : 200 , data : [ . get : Data ( ) ] )
let requestExpectation = expectationForRequestingMock ( & mock )
let completionExpectation = expectationForCompletingMock ( & mock )
mock . register ( )
URLSession . shared . dataTask ( with : URLRequest ( url : url ) ) . resume ( )
wait ( for : [ requestExpectation , completionExpectation ] , timeout : 2.0 )
登録されたすべての模擬をクリアできます:
Mocker . removeAll ( )
Carthageは、依存関係を構築し、バイナリフレームワークを提供する分散型依存関係マネージャーです。
次のコマンドを使用して、HomeBrewでCarthageをインストールできます。
$ brew update
$ brew install carthage
Carthageを使用してモッカーをXcodeプロジェクトに統合するには、 Cartfile
で指定します。
github "WeTransfer/Mocker" ~> 3.0.0
carthage update
実行してフレームワークを構築し、構築されたMocker.framework
Xcodeプロジェクトにドラッグします。
Swift Package Managerは、Swiftコードの配布を管理するためのツールです。 Swiftビルドシステムと統合されて、依存関係のダウンロード、コンパイル、リンクのプロセスを自動化します。
Package.swift
ファイルにパッケージとしてモッカーを追加し、使用するターゲットの依存関係として指定します。
import PackageDescription
let package = Package (
name : " MyProject " ,
platforms : [
. macOS ( . v10_15 )
] ,
dependencies : [
. package ( url : " https://github.com/WeTransfer/Mocker.git " , . upToNextMajor ( from : " 3.0.0 " ) )
] ,
targets : [
. target (
name : " MyProject " ,
dependencies : [ " Mocker " ] ) ,
. testTarget (
name : " MyProjectTests " ,
dependencies : [ " MyProject " ] ) ,
]
)
Xcodeプロジェクトへの依存関係としてモッカーを追加するには、 [ファイル]> [Swiftパッケージ]> [パッケージの依存関係の追加]を選択し、リポジトリURLを入力します。
次のエラーが発生した場合: Auto-Link Library XCTESTとXCTESTSWIFTSUPPORTが見つからない場合は、NOからYEにビルドオプションの下に次のプロパティを設定します。
enable_testing_search_paths to yes
前述の依存関係マネージャーを使用したくない場合は、モッカーをプロジェクトに手動で統合できます。
端末を開き、 cd
トップレベルのプロジェクトディレクトリに開き、次のコマンドを実行します。
$ git init
次のコマンドを実行して、gitサブモジュールとしてモッカーを追加します。
$ git submodule add https://github.com/WeTransfer/Mocker.git
新しいMocker
フォルダーを開き、 Mocker.xcodeproj
アプリケーションのXCodeプロジェクトのプロジェクトナビゲーターにドラッグします。
アプリケーションのBlue Projectアイコンの下にネストされているように見えます。他のすべてのXcodeグループの上または下にあるかどうかは重要ではありません。
プロジェクトナビゲーターでMocker.xcodeproj
選択し、アプリケーションターゲットの展開ターゲットと一致する展開ターゲットを確認します。
次に、Project Navigator(Blue Project Icon)でアプリケーションプロジェクトを選択して、ターゲット構成ウィンドウに移動し、サイドバーの「ターゲット」の下のアプリケーションターゲットを選択します。
そのウィンドウの上部にあるタブバーで、「一般」パネルを開きます。
「埋め込まれたバイナリ」セクションの下の+
ボタンをクリックします。
Mocker.framework
を選択します。
そしてそれだけです!
Mocker.framework
、シミュレーターとデバイスに基づいてビルドするために必要なコピーファイルビルドフェーズに、ターゲット依存性、リンクされたフレームワーク、組み込みフレームワークとして自動的に追加されます。
変更のリストについては、changelog.mdを参照してください。
モッカーはMITライセンスの下で利用できます。詳細については、ライセンスファイルを参照してください。