@@ -548,25 +548,20 @@ authentication service that:
5485482 . Calls an API to authenticate
5495493 . Stores tokens with expiration times
550550
551- In the example below, we'll create a full ` AuthService ` class that handles user
552- login, token management, and authentication. We 'll test it thoroughly using
553- various mocking techniques covered earlier: stubbing fetch requests, spying on
554- methods, and manipulating time to test token expiration - all within organized
555- test steps.
551+ In the example below, we'll create a full ` AuthService ` class in an application
552+ module that handles user login, token management, and authentication. Then we 'll
553+ import it from a test module and test it thoroughly using various mocking
554+ techniques covered earlier: stubbing fetch requests, spying on methods, and
555+ manipulating time to test token expiration, all within organized test steps.
556556
557557Deno's testing API provides a useful ` t.step() ` function that allows you to
558558organize your tests into logical steps or sub-tests. This makes complex tests
559559more readable and helps pinpoint exactly which part of a test is failing. Each
560560step can have its own assertions and will be reported separately in the test
561- output.
561+ output. First, define the service in its own module:
562562
563- ``` ts
564- import { assertEquals , assertRejects } from " jsr:@std/assert" ;
565- import { spy , stub } from " jsr:@std/testing/mock" ;
566- import { FakeTime } from " jsr:@std/testing/time" ;
567-
568- // The service we want to test
569- class AuthService {
563+ ``` ts title="auth_service.ts"
564+ export class AuthService {
570565 private token: string | null = null ;
571566 private expiresAt: Date | null = null ;
572567
@@ -587,13 +582,13 @@ class AuthService {
587582 throw new Error (` Authentication failed: ${response .status } ` );
588583 }
589584
590- const data = await response .json ();
585+ const data: { token : string } = await response .json ();
591586
592587 // Store token with expiration (1 hour)
593588 this .token = data .token ;
594589 this .expiresAt = new Date (Date .now () + 60 * 60 * 1000 );
595590
596- return this .token ;
591+ return data .token ;
597592 }
598593
599594 getToken(): string {
@@ -615,6 +610,15 @@ class AuthService {
615610 this .expiresAt = null ;
616611 }
617612}
613+ ```
614+
615+ Then import the service from your test file:
616+
617+ ``` ts title="auth_service_test.ts"
618+ import { assertEquals , assertRejects , assertThrows } from " jsr:@std/assert" ;
619+ import { assertSpyCalls , spy , stub } from " jsr:@std/testing/mock" ;
620+ import { FakeTime } from " jsr:@std/testing/time" ;
621+ import { AuthService } from " ./auth_service.ts" ;
618622
619623Deno .test (" AuthService comprehensive test" , async (t ) => {
620624 await t .step (" login should validate credentials" , async () => {
@@ -657,52 +661,49 @@ Deno.test("AuthService comprehensive test", async (t) => {
657661 }
658662 });
659663
660- await t .step (" token expiration should work correctly" , () => {
661- using fakeTime = new FakeTime ();
662-
664+ await t .step (" token expiration should work correctly" , async () => {
665+ using time = new FakeTime (new Date (" 2023-01-01T12:00:00Z" ));
663666 const authService = new AuthService ();
664- const time = fakeTime (new Date (" 2023-01-01T12:00:00Z" ));
667+
668+ const fetchStub = stub (
669+ globalThis ,
670+ " fetch" ,
671+ () =>
672+ Promise .resolve (
673+ new Response (JSON .stringify ({ token: " fake-token" }), {
674+ status: 200 ,
675+ headers: { " Content-Type" : " application/json" },
676+ }),
677+ ),
678+ );
679+
680+ const getTokenSpy = spy (authService , " getToken" );
665681
666682 try {
667- // Mock the login process to set token directly
668- authService .login = spy (
669- authService ,
670- " login" ,
671- async () => {
672- (authService as any ).token = " fake-token" ;
673- (authService as any ).expiresAt = new Date (
674- Date .now () + 60 * 60 * 1000 ,
675- );
676- return " fake-token" ;
677- },
683+ await authService .login (" user" , " pass" );
684+ assertEquals (authService .getToken (), " fake-token" );
685+ assertSpyCalls (getTokenSpy , 1 );
686+
687+ // Advance time past expiration
688+ time .tick (61 * 60 * 1000 );
689+
690+ // Token should now be expired
691+ assertThrows (
692+ () => authService .getToken (),
693+ Error ,
694+ " Token expired" ,
678695 );
679-
680- // Login and verify token
681- authService .login (" user" , " pass" ).then (() => {
682- const token = authService .getToken ();
683- assertEquals (token , " fake-token" );
684-
685- // Advance time past expiration
686- time .tick (61 * 60 * 1000 );
687-
688- // Token should now be expired
689- assertRejects (
690- () => {
691- authService .getToken ();
692- },
693- Error ,
694- " Token expired" ,
695- );
696- });
696+ assertSpyCalls (getTokenSpy , 2 );
697697 } finally {
698- time .restore ();
699- ( authService . login as any ) .restore ();
698+ fetchStub .restore ();
699+ getTokenSpy .restore ();
700700 }
701701 });
702702});
703703```
704704
705- This code defines ` AuthService ` class with three main functionalities:
705+ The ` auth_service.ts ` module defines an ` AuthService ` class with three main
706+ functionalities:
706707
707708- Login - Validates credentials, calls an API, and stores a token with an
708709 expiration time
@@ -711,7 +712,7 @@ This code defines `AuthService` class with three main functionalities:
711712
712713The testing structure is organized as a single main test with three logical
713714** steps** , each testing a different aspect of the service; credential
714- validation, API call handling and token expiration.
715+ validation, API call handling, and token expiration.
715716
716717🦕 Effective mocking is essential for writing reliable, maintainable unit tests.
717718Deno provides several powerful tools to help you isolate your code during
0 commit comments