This page is intended for users in Germany. Go to the page for users in United States.

iOS アプリのテストで簡単にEntityのFixtureを作れる Fixturable について


 こんにちは!iOS エンジニアの永田 (@ngtknt)です。今日は iOS アプリのテストにおける Fixture についてお話ししたいと思います。

Fixture とは

 前もってテストデータを用意しておき、様々な Example で使い回すことでよりテストを簡単に書くことができます。このテストデータのことを Fixture と呼びます。もちろんテストデータは画像などのバイナリデータもありますし、何かしらのレスポンスデータかもしれません。

何を解決する?

 今回は、Entity の Fixture について考えたいと思います。 ( Entity の定義については省略しますが、ドメインオブジェクトと考えてください) Entity の Fixture を定義すること自体はそこまで難しくないと思います。Entity を初期化して定数として定義するだけです。しかし、同時に一部のプロパティーだけ値を指定したいケースもありますよね。そういったケースにも対応したいとなると少し考える必要があります。そこで今回は以下の3点を解決する簡単な仕組みを考えたのでご紹介します。

  1. Entity の Fixture を提供するための Interface を揃える
  2. 使う側がプロパティーの値を設定せずに、適当なインスタンスを作ることができる
  3. 使う側がプロパティーの値を一部だけ設定しても、適当なインスタンスを作ることができる

Fixturable Protocol

 まずは、一貫した Interface を作るために Protocol を作ります。Fixturable Protocol は Self 型の static 変数 fixture を持ちます。

protocol Fixturable {
static var fixture: Self { get }
}

 例えば、以下のような User という Entity があったとするとします。

struct User {
let id: Int
let name: String
let email: String
let profile: Profile
}

 User Entity に Fixturable を準拠させます。ここでポイントは、すでにProfile Entity も Fixturable に準拠しているので .fixture と書くだけで済む点です。

extension User: Fixturable {
static let fixture = User(
id: 1,
name: "John Smith",
email: "john@example.com",
profile: .fixture
)
}

 ここまでで 1. 「Entity の Fixture を提供するための Interface を揃える」と 2. 「使う側がプロパティーの値を設定せずに、適当なインスタンスを作ることができる」が達成できました。

fixtureメソッドを自動生成

 さて、続いて 3. の「使う側がプロパティーの値を一部だけ設定しても、適当なインスタンスを作ることができる」を解決したいと思います。これも実はそこまで難しくないです。先ほど作成したfixture
変数を使ってメソッドの引数のデフォルト値を設定していきます。これによって自分で指定したいプロパティーのみ指定することができ、それ以外は適当なデフォルト値で初期化されます。

extension User: Fixturable {
static func fixture(
id: Int = fixture.id,
name: String = fixture.name,
email: String = fixture.email,
profile: Profile = fixture.profile
) {
return User(
id: id,
name: name,
email: email,
profile: profile
)
}
}

以下のような呼び出し、または結果が得られます。

User()
// ▿ User
// - id : 1
// - name: "John Smith"
// - email: "john@example.com"
// ▿ profile: ...

User(name: "Kento Nagata")
// ▿ User
// - id : 1
// - name: "Kento Nagata"
// - email: "john@example.com"
// ▿ profile: ...

User(id: 360748, email: "nagata@example.com")
// ▿ User
// - id : 360748
// - name: "John Smith"
// - email: "nagata@
example.com"
// ▿ profile: ...

 さて、ここでお気づきでしょうが、この定義自体は機械的に行うことができます。なので、コード生成ツールである Sourcery を利用します。Sourcery は、SourceKit が生成するコードの抽象定義を利用し、テンプレートからコードを生成します。Sourcery の詳細やできることに関してはSourceryのドキュメントをご覧ください。
 以下のテンプレート(swifttemplate)を書くことによって、上記のコードを自動生成できます。types.structs.filter({ $0.implements[“Fixturable”] != nil })とあるようにFixturableを実装した型のみを対象とします。

// fixturable.swifttemplate

// Import required module here

<%_ for type in types.structs.filter({ $0.implements[“Fixturable”] != nil }) { -%>

// MARK: - <%= type.name %> Fixturable

extension <%= type.name %> {
static func fixture(
<%_ for (index, variable) in type.storedVariables.enumerated() { -%>
<%= variable.name %>: <%= variable.typeName.name %> = fixture.<%= variable.name %><%= index == type.storedVariables.count - 1 ? “” : “,” %>
<%_ } -%>
) -> <%= type.name %> {
return <%= type.name %>(
<%_ for (index, variable) in type.storedVariables.enumerated() { -%>
<%= variable.name %>: <%= variable.name %><%= index == type.storedVariables.count - 1 ? “” : “,” %>
<%_ } -%>
)
}
}
<%_ } -%>

まとめ

さて、最後に簡単にまとめます。

  • Fixturable Protocolを作ることによって Entity の Fixture に関するInterfaceを揃える
  • fixture メソッドの引数のデフォルト値として fixture 変数を利用することによって、一部プロパティー値を設定可能な Fixture を取得できるようになる
  • fixture メソッドは、Sourceryを利用して自動生成可能

もし同じようなユースケースでお困りの方がいればご活用ください。

Wantedly, Inc.s Stellenangebote
Anonymous
Ce365374 03fe 498d 9cfa 1646a16eb2f5?1526996904
C2f95d84 c781 4c2d 8a6c 4acddf4ed0dd
8489e856 647d 43c6 b196 fb8336d1873e?1529041022
9fb598fe 1684 4714 94fc 2a3a82b6cbec
539293 379549055410133 448634375 n
10 Likes
Anonymous
Ce365374 03fe 498d 9cfa 1646a16eb2f5?1526996904
C2f95d84 c781 4c2d 8a6c 4acddf4ed0dd
8489e856 647d 43c6 b196 fb8336d1873e?1529041022
9fb598fe 1684 4714 94fc 2a3a82b6cbec
539293 379549055410133 448634375 n
10 Likes

iOS

Wöchentliches Ranking

Weitere Rankings anzeigen

Page top icon