Movatterモバイル変換


[0]ホーム

URL:


Skip to main content
⌘K
Up or down tonavigateEnter toselectEscape toclose
On this page

Stubbing in tests

Stubbing is a powerful technique for isolating the code you're testing byreplacing functions with controlled implementations. Whilespies monitor function calls withoutchanging behavior, stubs go a step further by completely replacing the originalimplementation, allowing you to simulate specific conditions or behaviors duringtesting.

What are stubs?Jump to heading

Stubs are fake implementations that replace real functions during testing. Theylet you:

  • Control what values functions return
  • Simulate errors or specific edge cases
  • Prevent external services like databases or APIs from being called
  • Test code paths that would be difficult to trigger with real implementations

Deno provides robust stubbing capabilities through theStandard Library's testing tools.

Basic stub usageJump to heading

Here's a simple example demonstrating how to stub a function:

import{ assertEquals}from"jsr:@std/assert";import{ stub}from"jsr:@std/testing/mock";// Original functionfunctiongetUserName(id:number):string{// In a real app, this might call a databasereturn"Original User";}// Function under testfunctiongreetUser(id:number):string{const name=getUserName(id);return`Hello,${name}!`;}Deno.test("greetUser with stubbed getUserName",()=>{// Create a stub that returns a controlled valueconst getUserNameStub=stub(globalThis,"getUserName",()=>"Test User");try{// Test with the stubbed functionconst greeting=greetUser(123);assertEquals(greeting,"Hello, Test User!");}finally{// Always restore the original function    getUserNameStub.restore();}});

In this example, we:

  1. Import the necessary functions from Deno's standard library
  2. Create a stub for thegetUserName function that returns "Test User" insteadof calling the real implementation
  3. Call our function under test, which will use the stubbed implementation
  4. Verify the result meets our expectations
  5. Restore the original function to prevent affecting other tests

Using stubs in a testing scenarioJump to heading

Let's look at a more practical example with aUserRepository class thatinteracts with a database:

import{ assertSpyCalls, returnsNext, stub}from"jsr:@std/testing/mock";import{ assertThrows}from"jsr:@std/assert";typeUser={  id:number;  name:string;};// This represents our database access layerconst database={getUserById(id:number): User|undefined{// In a real app, this would query a databasereturn{ id, name:"Ada Lovelace"};},};// The class we want to testclassUserRepository{staticfindOrThrow(id:number): User{const user= database.getUserById(id);if(!user){thrownewError("User not found");}return user;}}Deno.test("findOrThrow method throws when the user was not found",()=>{// Stub the database.getUserById function to return undefined  using dbStub=stub(database,"getUserById",returnsNext([undefined]));// We expect this function call to throw an errorassertThrows(()=> UserRepository.findOrThrow(1), Error,"User not found");// Verify the stubbed function was called onceassertSpyCalls(dbStub,1);});

In this example:

  1. We're testing thefindOrThrow method, which should throw an error when auser is not found
  2. We stubdatabase.getUserById to returnundefined, simulating a missinguser
  3. We verify thatfindOrThrow throws the expected error
  4. We also check that the database method was called exactly once

Note that we're using theusing keyword withstub, which is a convenient wayto ensure the stub is automatically restored when it goes out of scope.

Advanced stub techniquesJump to heading

Returning different values on subsequent callsJump to heading

Sometimes you want a stub to return different values each time it's called:

import{ returnsNext, stub}from"jsr:@std/testing/mock";import{ assertEquals}from"jsr:@std/assert";Deno.test("stub with multiple return values",()=>{const fetchDataStub=stub(    globalThis,"fetchData",// Return these values in sequencereturnsNext(["first result","second result","third result"]),);try{assertEquals(fetchData(),"first result");assertEquals(fetchData(),"second result");assertEquals(fetchData(),"third result");}finally{    fetchDataStub.restore();}});

Stubbing with implementation logicJump to heading

You can also provide custom logic in your stub implementations:

import{ stub}from"jsr:@std/testing/mock";import{ assertEquals}from"jsr:@std/assert";Deno.test("stub with custom implementation",()=>{// Create a counter to track how many times the stub is calledlet callCount=0;const calculateStub=stub(    globalThis,"calculate",(a:number, b:number)=>{      callCount++;return a+ b*2;// Custom implementation},);try{const result=calculate(5,10);assertEquals(result,25);// 5 + (10 * 2)assertEquals(callCount,1);}finally{    calculateStub.restore();}});

Stubbing API calls and external servicesJump to heading

One of the most common uses of stubs is to replace API calls during testing:

import{ assertEquals}from"jsr:@std/assert";import{ stub}from"jsr:@std/testing/mock";asyncfunctionfetchUserData(id:string){const response=awaitfetch(`https://api.example.com/users/${id}`);if(!response.ok){thrownewError(`Failed to fetch user:${response.status}`);}returnawait response.json();}Deno.test("fetchUserData with stubbed fetch",async()=>{const mockResponse=newResponse(JSON.stringify({ id:"123", name:"Jane Doe"}),{ status:200, headers:{"Content-Type":"application/json"}},);// Replace global fetch with a stubbed versionconst fetchStub=stub(    globalThis,"fetch",()=>Promise.resolve(mockResponse),);try{const user=awaitfetchUserData("123");assertEquals(user,{ id:"123", name:"Jane Doe"});}finally{    fetchStub.restore();}});

Best practicesJump to heading

  1. Always restore stubs: Usetry/finally blocks or theusing keyword toensure stubs are restored, even if tests fail.

  2. Use stubs for external dependencies: Stub out database calls, APIrequests, or file system operations to make tests faster and more reliable.

  3. Keep stubs simple: Stubs should return predictable values that let youtest specific scenarios.

  4. Combine with spies when needed: Sometimes you need to both replacefunctionality (stub) and track calls (spy).

  5. Stub at the right level: Stub at the interface boundary rather than deepwithin implementation details.

🦕 Stubs are a powerful tool for isolating your code during testing, allowingyou to create deterministic test environments and easily test edge cases. Byreplacing real implementations with controlled behavior, you can write morefocused, reliable tests that run quickly and consistently.

For more testing resources, check out:

Did you find what you needed?

What can we do to improve this page?

If provided, you'll be @mentioned in the created GitHub issue

Privacy policy

[8]ページ先頭

©2009-2025 Movatter.jp