Integrationstest af tRPC-routere
Opsæt integrationstests for at kontrollere det korrekte samspil mellem forskellige tRPC-procedurer og deres dependencies.
Integrationstest af tRPC-routere er en gratis Typesikre API'er fra frontend til backend med tRPC-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Typesikre API'er fra frontend til backend med tRPC, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Typesikre API'er fra frontend til backend med tRPC-kurset indeholder 4 lektioner i alt.
Hvad er integrationstest?
Velkommen til integrationstest af tRPC! Hvor enhedstest kontrollerer individuelle funktioner, kontrollerer integrationstest, hvordan forskellige dele af din tRPC-applikation fungerer sammen.
Det indebærer ofte at teste samspillet mellem tRPC-procedurer og eksterne afhængigheder som en database eller en anden tjeneste.
Enhedstest kontra integrationstest
Lad os tydeliggøre forskellen:
- Enhedstest: Fokuserer på at isolere en enkelt tRPC-procedure. De moker alle eksterne afhængigheder for kun at teste procedurens logik.
- Integrationstest: Tester flowet mellem flere procedurer eller mellem en procedure og en rigtig (eller mock'et) ekstern afhængighed, f.eks. for at sikre, at en ændring opdaterer en database korrekt.
Opsætning til integrationstest
Til integrationstest har du brug for et kontrolleret miljø. Det betyder ofte:
- At bruge en database i hukommelsen eller en dedikeret testdatabaseinstans.
- Kun at mocke nødvendige eksterne tjenester, ikke centrale afhængigheder som din database.
- At opsætte en tRPC-test-caller til at kalde procedurer direkte uden om HTTP-anmodninger.
Opret en tRPC-test-caller
For at kalde dine tRPC-procedurer direkte i test skal du oprette en test-caller. Dette objekt lader dig interagere med din router uden en komplet HTTP-server, hvilket gør testene hurtigere og lettere at sætte op.
Prøv at køre dette eksempel for at se en grundlæggende caller i funktion:
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<any>().create();
const publicProcedure = t.procedure;
const appRouter = t.router({
greeting: publicProcedure
.input(z.object({ name: z.string() }).optional())
.query(({ input }) => {
return `Hello, ${input?.name || 'world'}!`;
}),
addNumber: publicProcedure
.input(z.object({ num1: z.number(), num2: z.number() }))
.mutation(({ input }) => {
return input.num1 + input.num2;
}),
});
const caller = appRouter.createCaller({});
async function runSimulatedTest() {
console.log("Simulating tRPC integration test...");
const greetingResult = await caller.greeting.query({ name: "Coddy" });
console.log("Greeting result:", greetingResult);
const sumResult = await caller.addNumber.mutation({ num1: 5, num2: 3 });
console.log("Sum result:", sumResult);
}
runSimulatedTest();Test af en forespørgselsprocedure
Integrationstest af forespørgsler kontrollerer, at data hentes korrekt, ofte med en mock-database. Vi sikrer, at forespørgslen returnerer de forventede data baseret på den aktuelle tilstand.
Se, hvordan greeting-forespørgslen opfører sig med og uden input:
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<any>().create();
const publicProcedure = t.procedure;
const appRouter = t.router({
greeting: publicProcedure
.input(z.object({ name: z.string() }).optional())
.query(({ input }) => {
// In a real test, this might fetch from a DB
return `Hello, ${input?.name || 'world'}!`;
}),
});
const caller = appRouter.createCaller({});
async function testGreetingQuery() {
console.log("--- Testing 'greeting' query ---");
// Test with a name
const resultWithName = await caller.greeting.query({ name: "Alice" });
console.log("Result with name:", resultWithName);
// Test without a name (default behavior)
const resultNoName = await caller.greeting.query();
console.log("Result no name:", resultNoName);
console.log("Query tests complete.");
}
testGreetingQuery();Test af en ændringsprocedure
Ændringer modificerer data. En integrationstest af en ændring bør kontrollere, at dataene ændres som forventet, og at efterfølgende forespørgsler afspejler disse ændringer. Vi simulerer en database i hukommelsen.
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<any>().create();
const publicProcedure = t.procedure;
// Simulate an in-memory database
const _db: { messages: string[] } = { messages: [] };
const appRouter = t.router({
getMessages: publicProcedure
.query(() => _db.messages),
addMessage: publicProcedure
.input(z.object({ text: z.string() }))
.mutation(({ input }) => {
_db.messages.push(input.text);
return input.text;
}),
});
const caller = appRouter.createCaller({});
async function testAddMessageMutation() {
console.log("--- Testing 'addMessage' mutation ---");
// 1. Check initial state
let initialMessages = await caller.getMessages.query();
console.log("Initial messages:", initialMessages);
// 2. Perform mutation
const addedText = await caller.addMessage.mutation({ text: "Hello tRPC!" });
console.log("Added message:", addedText);
// 3. Check state after mutation
let updatedMessages = await caller.getMessages.query();
console.log("Updated messages:", updatedMessages);
// 4. Perform another mutation
await caller.addMessage.mutation({ text: "More data!" });
updatedMessages = await caller.getMessages.query();
console.log("Further updated messages:", updatedMessages);
console.log("Mutation tests complete.");
}
testAddMessageMutation();Kontrol af sammenflettede routere
I større tRPC-applikationer organiserer du din API i flere routere og fletter dem derefter sammen til en rodrouter. Integrationstest bør bekræfte, at procedurer fra forskellige sammenflettede routere kan tilgås og interagere korrekt.
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<any>().create();
const publicProcedure = t.procedure;
// Simulate an in-memory database
const _db: { users: string[]; products: string[] } = { users: [], products: [] };
// User router
const userRouter = t.router({
getUsers: publicProcedure.query(() => _db.users),
addUser: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(({ input }) => {
_db.users.push(input.name);
return input.name;
}),
});
// Product router
const productRouter = t.router({
getProducts: publicProcedure.query(() => _db.products),
addProduct: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(({ input }) => {
_db.products.push(input.name);
return input.name;
}),
});
// Root router merging user and product routers
const appRouter = t.router({
user: userRouter,
product: productRouter,
});
const caller = appRouter.createCaller({});
async function testMergedRouters() {
console.log("--- Testing merged routers ---");
// Add a user
await caller.user.addUser.mutation({ name: "Bob" });
const users = await caller.user.getUsers.query();
console.log("Users after adding Bob:", users);
// Add a product
await caller.product.addProduct.mutation({ name: "Laptop" });
const products = await caller.product.getProducts.query();
console.log("Products after adding Laptop:", products);
console.log("All users:", await caller.user.getUsers.query());
console.log("All products:", await caller.product.getProducts.query());
console.log("Merged router tests complete.");
}
testMergedRouters();Test med kontekstdata
Mange tRPC-procedurer afhænger af kontekst-objektet, som kan indeholde data som en godkendt brugers ID eller roller. I integrationstest angiver du en mock-kontekst, når du opretter din caller, for at simulere forskellige brugertilstande.
import { initTRPC, TRPCError } from '@trpc/server';
import { z } from 'zod';
// Define the shape of your context
interface MyContext {
userId?: string;
isAdmin?: boolean;
}
const t = initTRPC.context<MyContext>().create();
const publicProcedure = t.procedure;
const protectedProcedure = publicProcedure.use(
t.middleware(({ ctx, next }) => {
if (!ctx.userId) {
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Not authenticated' });
}
return next({ ctx: { ...ctx, userId: ctx.userId } });
})
);
const adminProcedure = protectedProcedure.use(
t.middleware(({ ctx, next }) => {
if (!ctx.isAdmin) {
throw new TRPCError({ code: 'FORBIDDEN', message: 'Not an admin' });
}
return next({ ctx: { ...ctx, isAdmin: ctx.isAdmin } });
})
);
const appRouter = t.router({
whoAmI: protectedProcedure.query(({ ctx }) => `You are user ${ctx.userId}`),
adminAction: adminProcedure.mutation(() => "Admin action successful!"),
});
async function testWithMockContext() {
console.log("--- Testing with mock context ---");
// 1. Test unauthenticated user
const unauthCaller = appRouter.createCaller({});
try {
await unauthCaller.whoAmI.query();
} catch (error: any) {
console.log("Unauthenticated user error:", error.message);
}
// 2. Test authenticated user
const authCaller = appRouter.createCaller({ userId: "user123" });
const userResult = await authCaller.whoAmI.query();
console.log("Authenticated user result:", userResult);
// 3. Test non-admin user trying admin action
try {
await authCaller.adminAction.mutation();
} catch (error: any) {
console.log("Non-admin user error:", error.message);
}
// 4. Test admin user
const adminCaller = appRouter.createCaller({ userId: "admin456", isAdmin: true });
const adminResult = await adminCaller.adminAction.mutation();
console.log("Admin user result:", adminResult);
console.log("Context tests complete.");
}
testWithMockContext();Oprydning af testdata
Integrationstest, der ændrer delt tilstand, f.eks. en database, kan skabe afhængigheder mellem test, hvis de ikke håndteres omhyggeligt. Det er afgørende at rydde alle oprettede data op efter hver test eller testsuite.
- Brug
beforeEach/afterEach-hooks i dit testrammeværk. - Nulstil mock-databaser, eller tøm tabeller, for at sikre, at testene er isolerede og kan gentages.
Udfordring i integrationstest
Forestil dig en tRPC-opsætning, hvor en userRouter håndterer oprettelse af brugere, og en postRouter håndterer oprettelse af indlæg, hvor indlæg kræver et gyldigt userId. Du er ved at skrive en integrationstest.
Opsummering: Integrationstest af tRPC
Vi har undersøgt, hvordan du udfører integrationstests af tRPC-routere. Du har lært at:
- Bruge en test-klient til at kalde procedurer direkte.
- Teste både forespørgsels- og ændringsprocedurer og observere ændringer i tilstanden.
- Bekræfte interaktioner på tværs af sammenføjede routere.
- Angive en mock-kontekst til godkendte tests.
- Forstå vigtigheden af oprydning i testdata.
Integrationstests er afgørende for at sikre, at komponenterne i din tRPC-API fungerer harmonisk og interagerer korrekt med afhængigheder.
Lær Typesikre API'er fra frontend til backend med tRPC med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 10
- Lektioner
- 40
Ofte stillede spørgsmål
Er lektionen “Integrationstest af tRPC-routere” gratis?
Ja — alle 3 lektioner i læringssporet Typesikre API'er fra frontend til backend med tRPC, inklusive “Integrationstest af tRPC-routere”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Typesikre API'er fra frontend til backend med tRPC-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Integrationstest af tRPC-routere”?
Opsæt integrationstests for at kontrollere det korrekte samspil mellem forskellige tRPC-procedurer og deres dependencies. Du øver dig i Typesikre API'er fra frontend til backend med tRPC med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Typesikre API'er fra frontend til backend med tRPC?
Der kræves ingen tidligere erfaring. Typesikre API'er fra frontend til backend med tRPC på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “Integrationstest af tRPC-routere”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Typesikre API'er fra frontend til backend med tRPC-lektion?
Ja. Alle Typesikre API'er fra frontend til backend med tRPC-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Enhedstest af tRPC-procedurer
- Integrationstest af tRPC-routere
- Strategier for end-to-end-test
- Mocking af context og dependencies i tRPC-tests