Se moquer et tester GraphQL dans React
Tester GraphQL ne doit pas être difficile. Cet article explore les méthodes statiques et dynamiques pour faciliter le test de GraphQL.
Imaginez que vous travailliez sur une nouvelle fonctionnalité avec une autre équipe. Vous êtes responsable du côté React, mais quelqu'un d'autre est responsable des modifications de GraphQL. Est-il possible pour vous de développer votre camp avant le s'ils ont fini le leur?
ou de vouloir tester vos composants sans envoyer de véritables requêtes GraphQL au serveur? Avec Mocks, les deux sont possibles! Les simulacres vous permettent de fournir de fausses réponses à vos requêtes, vous permettant ainsi de tester entièrement vos composants sans interagir avec un serveur réel.
Dans cet article, qui suppose des connaissances préalables de React et de GraphQL, nous allons nous intéresser de deux manières différentes. simulez des réponses à une requête GraphQL. Le premier est plus facile, mais peut être un peu plus rigide avec MockedProvider . La deuxième façon nous permet de définir de faux résolveurs et de générer nos données de test de manière dynamique. Une grande partie de mon inspiration pour cet article est venue d'une conférence donnée par Chang Wang à GraphQL Day Toronto.
Le code de base final est disponible ici: https: // github.com/leighhalliday/apollo-generating-types[19659006Faireunessaidecequenoustestons Nous allons travailler avec l'API GraphQL de Shopify pour afficher certains produits. La requête permettant d'extraire ces données se présente comme suit: Le composant qui exécute la requête ci-dessus et affiche ses résultats se présente comme suit: ID {product.id} Si vous souhaitez en savoir plus sur l'utilisation de TypeScript et Apollo GraphQL avec des types à génération automatique, veuillez vous reporter à de cet article . La première approche de moquage cette requête GraphQL doit utiliser quelque chose appelé un MockedProvider . En gros, il recherche une requête spécifique et, lorsqu'il voit cette requête, utilise une réponse prédéfinie. Vous vous retrouvez avec un tableau de répliques, chacune avec une requête et le résultat correspondant. Dans ce cas, j'ai importé la requête La fermeture de tous ces objets et tableaux peut s'avérer fastidieuse, mais l'objectif est de faire correspondre à la structure de données exactement comment vous vous attendez à le récupérer depuis le serveur. Avec Apollo, à chaque fois. vous utilisez le composant Si la bibliothèque de test-tests est nouvelle pour vous, j’ai écrit une introduction qui pourrait être utile. Alors que le Dans la section suivante, nous tenterons de surmonter ces inconvénients en utilisant une approche un peu plus compliquée, mais comportant de nombreuses améliorations. Si le Le schéma GraphQL définit le fonctionnement de l'API GraphQL: Quelles requêtes et quelles mutations peuvent être exécutées et quels types sont définis? Dans cette approche, nous allons commencer par saisir le schéma de l'API GraphQL, ce qui peut être fait à l'aide de la commande nous devons définir notre propre Next, nous pouvons définir notre composant Avec le La fonction Si une API GraphQL fournit des valeurs Scalar personnalisées ou si vous souhaitez fournir Selon vos propres valeurs, vous pouvez fournir des résolveurs factices personnalisés, ce qui permet une flexibilité totale par rapport à notre Dans ce cas, nous avons annulé ce que le champ Par défaut, chaque fois qu'il y a un tableau d'éléments, Apollo en retournera 2. Mais que se passe-t-il si vous voulez 0, 10 ou même un nombre variable d'éléments? C'est là que l'objet MockList entre en jeu. Cela nous permettra de définir exactement le nombre d'éléments que nous voulons. Dans ce cas, nous aurons entre 0 et 3 éléments de bord d'image dans notre réponse. Souvent, nos requêtes (et leurs champs) utilisent des arguments pour fournir des détails supplémentaires au serveur. Dans cette requête, l’API Shopify GraphQL nous permet de définir le type d’image (JPG, PNG, etc.) que nous souhaiterions obtenir en réponse. Voici comment accéder à ces arguments, ce qui vous permet de personnaliser votre résolveur simulé en fonction des arguments qui lui sont transmis: Nous pouvons maintenant donner à l'URL renvoyée par le résolveur du champ Plutôt que de définir chaque champ, vous pouvez utiliser une bibliothèque telle que faker pour fournir des données fausses plus réalistes. Parfois, vos données sont un peu trop aléatoires, cependant. Prenons l'exemple ci-dessous où nous utilisons la fonction Pour ce scénario, Dans cet article, nous avons vu deux manières différentes de simuler des requêtes GraphQL pour nos tests. La première approche utilisait le La deuxième approche consistait à créer une fonction export const PRODUCTS_QUERY = gql`
requête ProductsData ($ preferredContentType: ImageContentType) {
produits (premier: 10) {
bords {
noeud {
identifiant
Titre
images (premier: 3) {
bords {
noeud {
identifiant
transformerSrc (
largeur maximale: 150
maxHauteur: 100
preferredContentType: $ preferredContentType
)
}
}
}
}
}
}
}
`;
fonction d'exportation par défaut Products () {
revenir (
{product.title}
{product.images.edges.map (
({node: image}, index: number) => (
Utilisation de MockedProvider
PRODUCTS_QUERY
du fichier dans lequel elle est utilisée, en veillant à ce que je répète la même chose. valeurs de variable utilisées dans le composant que nous testons (sinon, il ne correspondra pas). // importations requises pour l'extrait de code ci-dessous
import {ImageContentType} de "./generated/globalTypes";
importer des produits, {PRODUCTS_QUERY} de "./Products";
const se moque = [{
request: {
query: PRODUCTS_QUERY,
variables: {
preferredContentType: ImageContentType.JPG
}
},
result: {
data: {
products: {
edges: [{
node: {
id: "123",
title: "Nike Shoes",
images: {
edges: [{
node: {
id: "456",
transformedSrc: "https://www.images.com/shoe.jpg"
}
}]
}
}
}]
}
}
}
}];
Query
. Pour qu'il puisse exécuter cette requête, il doit être situé dans un fournisseur
. Ce fournisseur fournit le contexte nécessaire pour résoudre les requêtes en cours d'exécution. C'est là qu'entre en jeu le MockedProvider
. Nous allons envelopper ce fournisseur autour de notre composant, ce qui permettra à nos simulacres de résoudre avec de fausses données plutôt que de passer un véritable appel d'API. it ("rend avec MockedProvider", async () => {
const {findByText, getByText} = render (
Inconvénients de MockedProvider
MockedProvider
vous permet d'être opérationnel rapidement. Il peut être fastidieux de définir toutes vos données pour chaque test et chaque scénario. Si vous souhaitez simuler 15 produits, vous devez définir une grande quantité de données simulées, puis, si vous souhaitez ajouter un champ supplémentaire, vous devez modifier chacun des 15 produits simulés. Ce genre de chose se fatigue très vite. Dynamic Mocks
MockedProvider
était un peu trop rigide à votre goût, vous serez peut-être intéressé de savoir qu'il existe un moyen de rendre les simulacres dynamiques! En termes généraux, cette approche prend un schéma GraphQL (défini manuellement ou, comme nous le verrons, téléchargé depuis l’API GraphQL actuelle via une requête d’introspection), et nous permet de définir des résolveurs simulés pour chaque type de données, avec autant ou autant que nécessaire. Obtention du schéma
schema: download
fournie par le paquet apollo
. Nous allons nous retrouver avec un fichier schema.json
à la racine de notre projet, contenant l'intégralité de la sortie de l'API introspectée. yarn run apollo schema: download
--endpoint https://graphql.myshopify.com/api/graphql
--header "Jeton d'accès X-Shopify-Store: 078bc5caa0ddebfa89cccb4a1baa1f5c"
Création du modèle AutoMockedProvider
AutoMockedProvider
. Un grand nombre d'importations sont nécessaires pour cette fonctionnalité, mais nous allons explorer ce que chacun fait lorsqu'il le faut. import React, {ReactNode} from "react";
importer {ApolloProvider} de "react-apollo";
importer {ApolloClient} de "apollo-client";
importer {InMemoryCache} de "apollo-cache-inmemory";
importer {SchemaLink} de "apollo-link-schema";
import {makeExecutableSchema, addMockFunctionsToSchema, IMocks} à partir de "graphql-tools";
importer {printSchema, buildClientSchema} de "graphql / utilities";
import introspectionResult de "../../schema.json";[
AutoMockedProvider
. J'ai supprimé certaines définitions TypeScript pour permettre au code de se lire un peu plus proprement, mais si TypeScript vous intéresse, je les ai laissées dans la base de code actuelle de GitHub. fonction par défaut d'exportation AutoMockedProvider ({children, mockResolvers }) {
// 1) Conversion du schéma JSON en langage de définition de schéma
const schemaSDL = printSchema (
buildClientSchema ({__schema: introspectionResult .__ schéma})
)
// 2) Rendre le schéma "exécutable"
const schema = makeExecutableSchema ({
typeDefs: schemaSDL,
resolverValidationOptions: {
requireResolversForResolveType: false
}
});
// 3) Appliquer des résolveurs factices au schéma exécutable
addMockFunctionsToSchema ({schéma, mock: mockResolvers});
// 4) Définir ApolloClient (variable client utilisée ci-dessous)
const client = new ApolloClient ({
lien: new SchemaLink ({schema}),
cache: new InMemoryCache ()
});
retour
AutoMockedProvider
défini, nous pouvons l’utiliser comme notre fournisseur Apollo
mais comme nous le verrons dans la section suivante, c’est là que le plaisir et la souplesse commence.
Priorité aux résolveurs
addMockFunctionsToSchema
fournit immédiatement des résolveurs par défaut pour tous les types de base Scalar fournis avec GraphQL ( String
ID
Boolean
etc.). Cela signifie que, par défaut, une chaîne sera résolue en Hello World
et que chaque autre type a sa propre valeur par défaut. AutoMockedProvider
. it ("rend avec AutoMockedProvider", async () => {
const mockResolvers = {
Produit: () => ({titre: "Chaussures Nike"}),
URL: () => "https://www.shopify.com"
};
const {findByText, getByText} = render (
title
du type Product
sera et aura fourni un résolveur pour le type scalaire personnalisé . URL
. Une erreur se produira si aucun résolveur personnalisé n'est fourni pour les types Scalar personnalisés. Personnalisation des éléments de tableau avec MockList
const mockResolvers = {
Produit: () => ({
titre: "Nike Shoes",
images: () => ({
bords: () => new MockList ([0, 3])
})
})
};
Accès aux arguments
const mockResolvers = {
Image: () => ({
transformerSrc: (root, {preferredContentType}) => `https://images.com/cat.$ {preferredContentType.toLowerCase ()}`
})
transformerSrc
l'extension correspondant à l'argument transmis au champ ( .jpg
dans ce cas). Fausses valeurs cohérentes
uuid
de faker pour générer chaque ID, dans le but de produire un test d'instantané. Chaque fois que le code sera exécuté, nous aurons des UUID uniques, ce qui rendra difficile d'avoir un instantané cohérent. faker
fournit un moyen de définir une valeur initiale, en veillant à chaque fois que ce code est exécuté, il fournira une sortie aléatoire mais cohérente faker.seed (123)
. it ("correspond à l’instantané utilisant des graines", async () => {
faker.seed (123);
const {findByTestId, asFragment} = render (
Conclusion
MockedProvider
nous permettant de définir explicitement quelles données seraient renvoyées pour chaque requête. Cela fonctionne bien mais peut rapidement devenir fastidieux et difficile à maintenir. AutoMockedProvider
à l'aide de la fonction addMockFunctionsToSchema
d'Apollo, nous permettant de définir et d'annuler les résolvers. pour chaque type de données et chaque champ, il suffit de les définir explicitement lorsque cela est nécessaire. D'après mon expérience, c'est la voie à suivre, offrant une flexibilité extrême sans trop de frais généraux supplémentaires.
Source link