前端模拟后端api_通过使用测试工具模拟整个api来构建前端应用程序

前端模拟后端api

您不需要后端…(还) (You don’t need a backend…(yet))

If you’re a frontend developer, you’ve probably experienced being blocked while waiting on the backend API to be built. You could just hard code the templates and leave the integration until later, or import a JSON file of mock data to use, but neither of these solutions are particularly efficient as they don’t allow you to see the result of the actual code that will run in production.

如果您是前端开发人员,则在等待构建后端API时可能会遇到被阻止的情况。 您可以对模板进行硬编码,然后再进行集成,直到稍后再导入,或者导入要使用的模拟数据的JSON文件,但是这些解决方案都不是特别有效,因为它们不允许您查看实际代码的结果。在生产中运行。

Even if your backend API is ready and you have a local development environment to work with, configuring your application and populating your database can be a laborious process.

即使您的后端API已经准备好并且您可以使用本地开发环境,配置应用程序和填充数据库也可能是一个费力的过程。

Luckily, Apollo Client ships with MockedProvider, a component that allows you to test your React components by mocking calls to your GraphQL endpoint. How does this help? The idea came to me one day while I was writing unit tests — if theMockedProvider could be used to hydrate my React App in the test environment JSDOM, then why not in the browser DOM?

幸运的是, Apollo ClientMockedProvider一起MockedProvider ,该组件使您可以通过模拟对GraphQL端点的调用来测试React组件 。 这有什么帮助? 我有一天在编写单元测试的时候想到了这个主意-如果可以将MockedProvider用于测试环境JSDOM中的React App,那么为什么不使用浏览器DOM?

This probably is not the intended use for MockedProvider, as the Apollo documentation only covers implementing it in the context of a test environment. However, I set out to put my theory to the test and found that this method works really well, improving my workflow and efficiency.

这可能不是MockedProvider的预期用途,因为Apollo文档仅介绍了在测试环境中实施它的情况。 但是,我开始对我的理论进行检验,发现该方法确实非常有效,从而改善了我的工作流程和效率。

模式优先设计 (Schema first design)

The first thing you must do is agree on a schema with the team who will be building the backend. This will save a lot of refactoring later on, and if all goes according to plan you will be able to swap out the MockedProvider for the real one and your application should work seamlessly.

您必须做的第一件事就是与将要构建后端的团队就架构达成一致。 这将在以后节省大量的重构,并且如果一切按计划进行,您将可以将MockedProvider真正的MockedProvider ,并且您的应用程序应该可以无缝运行。

Since Apollo Client uses GraphQL, you can start by writing the GraphQL types. In the example below, we’ll be using a simple Book and Author model.

由于Apollo Client使用GraphQL,因此可以从编写GraphQL类型开始。 在下面的示例中,我们将使用一个简单的Book and Author模型。

import { SchemaLink } from '@apollo/client/link/schema'
import { makeExecutableSchema } from '@graphql-tools/schema'


import gql from 'graphql-tag'


const typeDefs = gql`
    type Query {
        books: [Book!]!
    }

    type Book {
        id: ID!
        title: String!
        publishDate: String!
        author: Author!
    }

    type Author {
        id: ID!
        firstName: String!
        lastName: String!
        avatarUrl: String!
    }
`
export const schemaLink = new SchemaLink({ schema: makeExecutableSchema({ typeDefs }) })

You can optionally create a client side schema to pass to your instance of Apollo Client, if you want to be able to inspect your schema with the Apollo Client Dev Tools.

如果您希望能够使用Apollo Client Dev Tools检查您的模式,则可以选择创建一个客户端模式以传递到您的Apollo Client实例。

import { ApolloClient, ApolloLink, InMemoryCache, createHttpLink } from '@apollo/client'
import { schemaLink } from './schema'


export const cache = new InMemoryCache()


const link = createHttpLink({
    /** Your graphql endpoint */
    uri: 'http://localhost:4000/',
})


export const client = new ApolloClient({
    connectToDevTools: true,
    link: ApolloLink.from([link, (schemaLink as unknown) as ApolloLink]),
    cache,
    resolvers: {},
    defaultOptions: {
        query: {
            errorPolicy: 'all',
        },
    },
})

生成TypeScript类型 (Generate TypeScript types)

This example is in TypeScript, so if you are using regular JS you can skip this step. GraphQL Code Generator is an excellent tool to convert GraphQL types into TypeScript types. You can use their web tool or install their CLI tool. Here is the output:

此示例在TypeScript中,因此,如果您使用常规JS,则可以跳过此步骤。 GraphQL代码生成器是将GraphQL类型转换为TypeScript类型的出色工具。 您可以使用其Web工具或安装其CLI工具。 这是输出:

export type Maybe<T> = T | null
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] }
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
    ID: string
    String: string
    Boolean: boolean
    Int: number
    Float: number
}


export type Query = {
    __typename?: 'Query'
    books: Array<Book>
}


export type Book = {
    __typename?: 'Book'
    id: Scalars['ID']
    title: Scalars['String']
    publishDate: Scalars['String']
    author: Author
}


export type Author = {
    __typename?: 'Author'
    id: Scalars['ID']
    firstName: Scalars['String']
    lastName: Scalars['String']
    avatarUrl: Scalars['String']
}

编写您的GraphQL查询 (Write your GraphQL query)

Now that we know the structure of our data, we can write our query to fetch all our books.

现在我们知道了数据的结构,我们可以编写查询来获取所有书籍。

import gql from 'graphql-tag'


export const GET_BOOKS = gql`
    query getBooks {
        books {
            id
            title
            publishDate
            author {
                id
                firstName
                lastName
                avatarUrl
            }
        }
    }
`

Note: If you are working with a REST API using apollo-link-rest you will need to include the @type directive in your queries to patch the __typename in your query results. For more info visit https://www.apollographql.com/docs/link/links/rest/#typename-patching

注意:如果使用的是使用apollo-link-rest的REST API,则需要在查询中包括@type指令,以在查询结果中修补__typename 。 有关更多信息,请访问https://www.apollographql.com/docs/link/links/rest/#typename-patching

创建您的模拟 (Create your mocks)

Apollo’s MockedProvider expects a mocks prop, which is an array of MockedResponse<T>. You could hard code these, but it is much less brittle to use a mock generator. We’ll be using Factory.ts with Faker, but you can use any libraries that suit your needs. Jose Silva has a good article explaining how to use Factory.ts and Faker with TDD.

阿波罗的MockedProvider期望一个mocks道具,其是阵列MockedResponse<T> 您可以对这些代码进行硬编码,但是使用模拟生成器的难度要小得多。 我们将在Faker中使用Factory.ts ,但是您可以使用任何适合您需要的库。 Jose Silva有一篇很好的文章,解释了如何在TDD中使用Factory.ts和Faker

For each GraphQL type we will create a factory, which will generate instances of that type. We can also provide the corresponding TypeScript type to the factory to validate the shape of the mock data. Faker will also be used to generate realistic data for each property on our Book and Author.

对于每种GraphQL类型,我们将创建一个工厂,该工厂将生成该类型的实例。 我们还可以向工厂提供相应的TypeScript类型,以验证模拟数据的形状。 Faker也将用于为BookAuthor上的每个属性生成现实数据。

Finally we can create our MockedResponse<Query> and leverage the buildList function on our factory to generate as many books as we want.

最后,我们可以创建我们的MockedResponse<Query>并利用工厂中的buildList函数生成所需数量的书。

import * as Factory from 'factory.ts'
import faker from 'faker'
import { Book, Author, Query } from './types'
import { MockedResponse } from '@apollo/client/testing'
import { GET_BOOKS } from './queries'


export const AuthorMock = Factory.Sync.makeFactory<Author>({
    __typename: 'Author',
    id: Factory.each(() => faker.random.uuid()),
    firstName: Factory.each(() => faker.name.firstName()),
    lastName: Factory.each(() => faker.name.lastName()),
    avatarUrl: Factory.each(() => faker.image.avatar()),
})


export const BookMock = Factory.Sync.makeFactory<Book>({
    __typename: 'Book',
    id: Factory.each(() => faker.random.uuid()),
    publishDate: Factory.each(() => faker.date.past().toISOString()),
    title: Factory.each(() => faker.random.words()),
    author: Factory.each(() => AuthorMock.build()),
})


export const booksQueryMock: MockedResponse<Query> = {
    request: {
        query: GET_BOOKS,
    },
    result: {
        data: {
            books: BookMock.buildList(10),
        },
    },
}

准备您的提供者 (Prepare your Provider)

You’ll want an easy way to switch between the real ApolloProvider and the MockedProvider. There are many ways you can do this, for example you might conditionally render it depending on the node env (dev/production).

您需要一种简单的方法在真实的ApolloProviderMockedProvider之间切换。 有许多方法可以执行此操作,例如,您可以根据节点env(dev / production)有条件地进行渲染。

In this example, I’m just going to use a basic boolean useMocks prop to switch between the two.

在这个示例中,我将使用一个基本的布尔useMocks在两者之间进行切换。

import React from 'react'
import { MockedProvider } from '@apollo/client/testing'
import { ApolloProvider } from '@apollo/client'
import { client } from '../../apollo/config'
import { booksQueryMock } from '../../apollo/mocks'


interface ProviderProps {
    useMocks?: boolean
}


export const Provider: React.FC<ProviderProps> = ({ useMocks, children }) => {
    if (useMocks)
        return (
            <MockedProvider mocks={[booksQueryMock]}>
                <>{children}</>
            </MockedProvider>
        )
    return (
        <ApolloProvider client={client}>
            <>{children}</>
        </ApolloProvider>
    )
}

Now wrap the main contents of your App with the Provider component, so that it can access the GraphQL data.

现在,使用Provider组件包装应用程序的主要内容,以便它可以访问GraphQL数据。

import React from 'react'
import { Books } from './components/Books/Books'
import { Provider } from './components/Provider/Provider'


export const App = () => {
    return (
        <Provider useMocks>
            <Books />
        </Provider>
    )
}

在组件中使用模拟数据 (Using the mock data in your component)

In our Books component we are going to use the useQuery hook to fetch the mock data from our MockedProvider.

在我们的Books组件中,我们将使用useQuery挂钩从MockedProvider获取模拟数据。

const { data, loading, error } = useQuery<Query>(GET_BOOKS)

If you console.log(data) you should be able to see the generated mock data in the browser console.

如果您使用console.log(data) ,则应该能够在浏览器控制台中看到生成的模拟数据。

Image for post

Now we can display this data in our component — the example below just renders it in a simple list.

现在,我们可以在组件中显示此数据-以下示例仅将其呈现在一个简单列表中。

import React from 'react'
import { useQuery } from '@apollo/client'
import { Query } from '../../apollo/types'
import { GET_BOOKS } from '../../apollo/queries'


export const Books: React.FC = () => {
    const { data, loading, error } = useQuery<Query>(GET_BOOKS)


    if (error) return <p>{error.message}</p>
    if (loading) return <p>Loading...</p>
    if (data)
        return (
            <ul>
                {data.books.map((book) => (
                    <li key={book.id}>
                        <h2>{`${book.title}`}</h2>
                        <img src={book.author.avatarUrl} width="50px" height="50px" alt="" role="presentation" />
                        <p>{`by ${book.author.firstName} ${book.author.lastName}`}</p>
                    </li>
                ))}
            </ul>
        )
    return null
}

You should be able to see your React app hydrated with mock data rendered in the browser.

您应该能够看到React应用程序与浏览器中呈现的模拟数据混合在一起。

Image for post

This is a very basic example, but you can also do more complex mocks with MockedProvider, such as paginated queries or mutations, by creating a MockedResponse<T> for each query or mutation. For mutations, you can create a mock factory to generate the mutation payload.

这是一个非常基本的示例,但是您还可以通过为每个querymutation创建MockedResponse<T>来使用MockedProvider进行更复杂的MockedProvider ,例如分页querymutation 。 对于突变,您可以创建一个模拟工厂来生成突变有效负载。

This makes it much easier to build the frontend, especially interactive UI which would normally be dependent on an API response, such as CRUD operations.

这使构建前端(特别是通常依赖于API响应(例如CRUD操作)的交互式UI)更加容易。

You can read more about how to use MockedProvider at:

您可以在以下位置阅读有关如何使用MockedProvider更多信息:

陷阱和常见错误 (Gotchas and common mistakes)

When working with MockedProvider I’ve come across a few idiosyncrasies that are good to be aware of to ensure it works properly.

在使用MockedProvider我遇到了一些特质,请MockedProvider注意以确保其正常工作。

  • If your queries or mutations require variables, for example, a pagination limit and offset, the mock you pass to the MockedProvider must have the exact same variables defined that the useQuery hook will be called with in your component.

    如果查询或变异需要变量,例如,分页限制和偏移量,则传递给MockedProvider的模拟必须具有与在组件中调用useQuery挂钩完全相同的变量。

  • If your mock data objects do not have the __typename field, then you must add the prop addTypeName={false} to your MockedProvider.

    如果模拟数据对象没有 __typename字段,则必须 prop addTypeName={false}MockedProvider

  • If your mock data objects do have the __typename field, you must remove the prop addTypeName={false} (it is true by default).

    如果模拟数据对象确实具有__typename字段,则必须删除 prop addTypeName={false} (默认情况下为true)。

结论 (Conclusion)

Using Apollo’s MockedProvider is an easy way to build frontend applications without relying on the backend API. There are a lot of benefits to this approach.

使用Apollo的MockedProvider是一种无需依赖后端API即可构建前端应用程序的简便方法。 这种方法有很多好处。

  • The code you write is the code that will be used in production — your queries and mutations will work exactly the same with both the MockedProvider and the ApolloProvider. You don’t need to comment out chunks of code that handle your API requests and replace it with an imported JSON file to hydrate your UI.

    您编写的代码就是将在生产环境中使用的代码-您的查询和变异将与MockedProviderApolloProvider 。 您无需注释掉处理您的API请求的代码块,而将其替换为导入的JSON文件即可对您的UI进行水化处理。

  • You can easily test the performance of your application — using your mock factories you can generate hundreds of entries of data to see how your frontend app renders it.

    您可以轻松测试应用程序的性能-使用模拟工厂,您可以生成数百个数据条目,以查看前端应用程序如何呈现它。
  • You can easily do visual checks for edge cases by tweaking your mock factories. For example what if some of the books have extremely long titles?

    您可以通过调整模拟工厂轻松地对边缘情况进行视觉检查。 例如,如果某些书的书名太长怎么办?
  • You can reuse your mocks factories for unit/integration tests.

    您可以将模拟工厂重新用于单元/集成测试。
  • Development time is much quicker as you don’t have to wait on API changes to write and test your code, and view the results in the browser.

    开发时间更快,因为您不必等待API更改即可编写和测试代码,并在浏览器中查看结果。

Although this article focuses specifically on Apollo Client, this approach should work for any API client that provides tools for mocking requests, making development and testing faster and easier.

尽管本文专门针对Apollo客户端,但是这种方法应适用于提供用于模拟请求的工具的任何API客户端,从而使开发和测试变得更快,更容易。

The full code repository for the examples shown in this article can be found at: https://github.com/laij84/apollo-mocked-provider

本文所示示例的完整代码存储库可在以下位置找到: https : //github.com/laij84/apollo-mocked-provider

翻译自: https://medium.com/swlh/building-frontend-applications-by-mocking-your-entire-api-with-testing-tools-2f050359677f

前端模拟后端api

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值