JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

Follow publication

Type-Safe Testing in Backends-for-Frontends

Prithwish Nath
JavaScript in Plain English
9 min readJul 5, 2023

--

What is it about BFFs that makes testing so important?

WunderGraph — A Primer

./.wundergraph/wundergraph.config.ts

// Data dependency #1 - API to get the capital of a country
const countries = introspect.graphql({
apiNamespace: "countries",
url: new EnvironmentVariable(
"COUNTRIES_URL",
"https://countries.trevorblades.com/"
),
});

// Data dependency #2 - API to get the weather of a given city
const weather = introspect.graphql({
apiNamespace: "weather",
url: new EnvironmentVariable(
"WEATHER_URL",
"https://weather-api.wundergraph.com/"
),
});

./.wundergraph/operations/CountryByCode.graphql

query ($code: String) {
countries_countries(filter: { code: { eq: $code } }) {
code
name
emojiU
}
}

./.wundergraph/operations/WeatherByCity.graphql

query ($city: String!) {
weather_getCityByName(name: $city, config: { units: metric }) {
name
country
weather {
summary {
title
}
temperature {
actual
feelsLike
min
max
}
}
}
}

./.wundergraph/operations/WeatherByCountryCapital.ts

import { createOperation, z } from "../generated/wundergraph.factory";

export default createOperation.query({
input: z.object({
code: z.string(),
}),
handler: async ({ input, operations }) => {

const country = await operations.query({
operationName: "CountryByCode",
input: {
code: input.code,
},
});
const weather = await operations.query({
operationName: "WeatherByCity",
input: {
city: country.data?.countries_countries[0]?.capital || ""
},
});
return {
country: country.data?.countries_countries[0].name,
weather: weather.data?.weather_getCityByName?.weather,
};
},
});
const { data, isLoading } = useQuery({
operationName: "CountryByCode",
input: {
code: "DE", // insert country ISO code here
},
});

return(
<div>


{isLoading ? (
<CardSkeleton />
) : (
<Card
code={data?.countries_countries[0].code}
name={data?.countries_countries[0].name}
/>
)}
...
)

WunderGraph’s Type-safe Testing Library

./test/dataDependencies.test.ts

./test/BFF.test.ts

Mocking

import { expect, describe, it, beforeAll, afterAll } from "@jest/globals";

import {
createTestAndMockServer,
TestServers,
} from "../.wundergraph/generated/testing";


let testServer: TestServers;

beforeAll(async () => {
testServer = createTestAndMockServer();
return testServer.start({
mockURLEnvs: ["COUNTRIES_URL", "WEATHER_URL"],
});
});

afterAll(() => testServer.stop());
...
// Step 1 : Set up the Mock
it("Should be able to get country based on mocked country code", async () => {
const scope = testServer.mockServer.mock({
times: 1,
persist: false,
match: ({ url, method }) => {
return url.path === "/" && method === "POST";
},
handler: async ({ json }) => {
const body = await json();

expect(body.variables.code).toEqual("DE");
expect(body.query).toEqual(
"query($code: String){countries_countries: countries(filter: {code: {eq: $code}}){code name capital}}"
);

return {
body: {
data: {
countries_countries: [
{
code: "DE",
name: "Germany",
capital: "Berlin",
},
],
},
},
};
},
});

// Step 2 : Call the real WunderGraph mounted endpoint for this operation (it'll be intercepted)

// Step 3 : Assert the mocked response

./test/dataDependenciesMocked.test.ts

E2E Testing


/* Run your BFF + Frontend before starting the tests */
webServer: process.env.WG_NODE_URL
? undefined
: {
command: 'npm run start',
port: 3000,
},
//...

Where to go from here?

--

--

Published in JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

Written by Prithwish Nath

Fullstack developer, audiophile, strategy game enthusiast.

No responses yet

Write a response