4/19/2016著者Jonas Helfer
これは、MeteorでApolloに取り組んでいるエンジニア、Jonas Helferによる寄稿記事です。
バックエンドのモック作成は常に面倒な作業だと思いますか?もしそうなら、この記事を読むことで考え方が変わるかもしれません…
モック作成とは、コンポーネントの偽バージョンを作成する手法であり、アプリケーションの他の部分を独立して開発およびテストできます。バックエンドのモック作成は、フロントエンドのプロトタイプを迅速に構築する優れた方法であり、サーバーを起動せずにフロントエンドをテストできます。APIモックは非常に便利なので、Googleで簡単に検索すると、多くの高価な製品やサービスがその支援を約束していることがわかります。
残念ながら、既存のソリューションのどれもが、本来あるべきほど簡単ではないと思います。結局のところ、それは間違ったテクノロジーでそれを実現しようとしていたからです。!
編集者注:この記事の概念は正確ですが、一部のコードサンプルは新しい使用方法パターンを示していません。読み終えたら、graphql-tools のドキュメントを参照して、現在のモック作成方法を確認してください。
バックエンドが返すデータをモックすることは、主に2つの理由で非常に便利です。
バックエンドAPIのモック作成には明確な利点があるのに、なぜ誰もがそれを行わないのでしょうか?それは、モック作成が多くの手間がかかるように思えるためだと思います。
バックエンドがブラウザからHTTP経由で呼び出されるREST APIであるとしましょう。バックエンドに取り組んでいる人がいて、フロントエンドに取り組んでいる人がいます。バックエンドコードは実際には各RESTエンドポイントで返されるデータの形状を決定しますが、モック作成はフロントエンドコードで行う必要があります。つまり、両方が同時に変更されない限り、バックエンドが変更されるたびにモックコードが壊れます。さらに悪いことに、バックエンドと最新の状態になっていないモックバックエンドに対してフロントエンドテストを実行している場合、テストはパスするかもしれませんが、実際のフロントエンドは機能しません。
より多くの依存関係を最新の状態に維持する必要がないため、簡単な方法は、REST APIをモックしないこと、またはバックエンドが自分自身でモックを担当することです。すべてが1か所にまとめられるためです。それは簡単かもしれませんが、速度も低下します。
プロジェクトでバックエンドをモックしない理由としてよく聞くもう1つの理由は、設定に時間がかかることです。まず、モックのオンオフを切り替えられるデータ取得レイヤーに追加のロジックを含める必要があり、次に、そのモックデータがどのようなものになるかを実際に記述する必要があります。多くの面倒な作業を必要とする重要なAPIの場合。
バックエンドのモック作成が難しい理由のこれら2つの理由は、実際には同じ根本的な理由によるものです。マシンで読み取れる形式で標準のREST APIの説明がなく、モックに必要なすべての情報を含んでおり、バックエンドとフロントエンドの両方で使用できます。SwaggerのようなAPI記述標準はありますが、必要なすべての情報が含まれているわけではなく、記述と維持が面倒になる可能性があります。サービスや製品にお金を払いたくない限り、そして場合によってはそれでも、モック作成は多くの作業になります。
実際には、モック作成は以前は多くの作業でした。なぜなら、新しいテクノロジーがAPIの考え方を変えているからです。GraphQLです。
GraphQLは、すべてのGraphQLバックエンドが静的な型システムを備えているため、モック作成を容易にします。型はバックエンドとフロントエンド間で共有でき、モック作成を非常に高速かつ便利にするために必要なすべての情報が含まれています。GraphQLを使用すると、開発またはテストのためにバックエンドをモックしない言い訳はありません。
新しいGraphQLサーバーツールキットの一部として構築しているGraphQLモックツールを使用して、モックバックエンドを作成する簡単な方法を次に示します。
// > npm install graphql-toolsimport { mockServer } from "graphql-tools"import schema from "./mySchema.graphql"
const myMockServer = mockServer(schema)myMockServer.query(`{ allUsers: { id name }}`)
// returns// {// data: {// allUsers:[// { id: 'ee5ae76d-9b91-4270-a007-fad2054e2e75', name: 'lorem ipsum' },// { id: 'ca5c182b-99a8-4391-b4b4-4a20bd7cb13a', name: 'quis ut' }// ]// }// }
すべてのGraphQLサーバーにはスキーマが必要なので、モック作成のために記述する必要がある追加のコードではありません。そして、クエリはコンポーネントがデータの取得に既に使用しているものなので、モック作成のために記述するコードでもありません。import文を除けば、バックエンド全体をモックするために1行のコードしかかかりませんでした。!
それを、ほとんどのREST APIと比較してください。REST APIでは、モック作成はURLを解析し、各エンドポイントに対してカスタム形状でデータ返すことを意味します。現実的なデータ返す単一のエンドポイントをモックするには、数十行のコードが必要です。GraphQLでは、形状はクエリにエンコードされ、スキーマと合わせてサーバーを1行のコードでモックするのに十分な情報があります。
送信できる有効なGraphQLクエリに対してモックデータ返すのに、この1行だけで済むと述べましたか?有効なクエリの一部ではなく、有効なクエリすべてです。!かなりクールですね?
上記の例では、モックサーバーはクエリを実行するたびに完全にランダムなIDと文字列を返します。アプリの構築を開始したばかりで、さまざまな状態でのUIコードの外観だけを確認したい場合は、おそらくそれで十分ですが、レイアウトの微調整を開始したり、モックサーバーを使用してコンポーネントのロジックをテストしたい場合は、より現実的なデータが必要になるでしょう。
幸いなことに、これは少しの努力だけで済みます。モックデータのカスタマイズは、Apolloモックツールが本当に光る場所です。なぜなら、返すモックデータについて事実上すべてをカスタマイズできるからです。
以下すべてを行うことができます。
// customize mocking per type (i.e. Integer, Float, String)mockServer(schema, { Int: () => 6, Float: () => 22.1, String: () => 'Hello',});
// customize mocking per field in the schema (i.e. for Person.name and Person.age)mockServer(schema, { Person: () => ({ name: casual.name, age: () => casual.integer(0,120), })});
// mock lists of specific or random length( and lists of lists of lists …)mockServer(schema, { Person: () => { // a list of length between 2 and 6 friends: () => new MockList([2,6]), // a list of three lists of two items: [[1, 1], [2, 2], [3, 3]] listOfLists: () => new MockList(3, () => new MockList(2)), },});
// customize mocking of a field or type based on the query argumentsmockServer(schema, { Person: () => { // the number of friends in the list now depends on numPages paginatedFriends: (o, { numPages }) => new MockList(numPages * PAGE_SIZE), },});
// You can also disable mocking for specific fields, pass through to the backend, etc.
各型と各フィールドについて、モックデータの生成を呼び出す関数を提供できます。フィールドのモック関数は型のモック関数よりも優先されますが、非常にうまく連携します。フィールドモック関数は、それらにとって重要なオブジェクトのプロパティのみを記述する必要があります。型のモック関数は残りを埋め込みます。
モック関数は実際には、偽装されたGraphQL解決関数です。つまり、モックはGraphQL解決関数内で実行できることを何でも実行できます。必要であれば、それを使ってバックエンド全体を記述できます。そうすべきだと言っているわけではありませんが、可能です。
このツールの真の力は、ほぼ任意に複雑なカスタマイズを許可しながら、非常に迅速に開始し、必要に応じて小さなステップでモックの洗練度を高めることができることです。各ステップは非常に簡単なので、非常に簡単だと感じるでしょう。
しかし、十分に話したので、完全な例を次に示します。
import { mockServer, MockList } from "graphql-tools"import casual from "casual-browserify"
// The GraphQL schema. Described in more detail here:// https://medium.com/apollo-stack/the-apollo-server-bc68762e93bconst schema = ` type User { id: ID! name: String lists: [List] } type List { id: ID! name: String owner: User incomplete_count: Int tasks(completed: Boolean): [Task] } type Task { id: ID! text: String completed: Boolean list: List } type RootQuery { user(id: ID): User } schema { query: RootQuery }`
// Mock functions are defined per type and return an// object with some or all of the fields of that type.// If a field on the object is a function, that function// will be used to resolve the field if the query requests it.const server = mockServer(schema, { RootQuery: () => ({ user: (o, { id }) => ({ id }), }), List: () => ({ name: () => casual.word, tasks: () => new MockList(4, (o, { completed }) => ({ completed })), }), Task: () => ({ text: casual.words(10) }), User: () => ({ name: casual.name }),})
mockServer.query(`query tasksForUser{ user(id: 6) { id name lists { name completeTasks: tasks(completed: true) { completed text } incompleteTasks: tasks(completed: false) { completed text } anyTasks: tasks { completed text } } }}`)
動作中の例とその生成される出力を確認するには、ライブデモにアクセスして、いくつかのクエリを実行してみてください。!
例を試したい場合は、Launchpad UIの「ダウンロード」ボタンをクリックするだけです。その仕組みについて知りたい場合、またはGraphQLのために構築しているその他のツールを確認したい場合は、apollostack/graphql-toolsにアクセスしてください。
かなりクールですね?それらはすべて、型システムを使用することで可能になります。そして、それはほんの始まりです。モックと現実のギャップを埋め、モックサーバーが機能を追加するにつれて徐々に実際のサーバーに変換されるように取り組んでいます。
この記事は、もともとApolloブログに掲載されました。私たちは毎週1つか2つの投稿を公開しており、取り組んでいることや考えていることについて書いています。