Lesson 29 of 49 intermediate

Testing — Trust Your Code

Break Things Before Users Do

Open interactive version (quiz + challenge)

Real-world analogy

Testing is like a fire drill. You don't wait for a real fire to find out if the exit doors work! You practice (test) regularly so when something goes wrong, you KNOW everything works. Untested code is like a building that's never had a fire drill — scary! 🔥

What is it?

Testing means writing code that verifies your other code works correctly. Jest is the most popular testing framework for JavaScript/TypeScript, and NestJS has built-in testing utilities.

Real-world relevance

Companies like Google require tests for every code change. The saying goes: 'Code without tests is broken by design.' A good test suite lets you refactor fearlessly!

Key points

Code example

// 1. Unit test — test a function in isolation 🔬
describe('calculateDiscount', () => {
  it('should apply 10% discount for orders over $100', () => {
    expect(calculateDiscount(200)).toBe(180);
  });

  it('should not discount orders under $100', () => {
    expect(calculateDiscount(50)).toBe(50);
  });

  it('should handle zero gracefully', () => {
    expect(calculateDiscount(0)).toBe(0);
  });
});

// 2. NestJS service test — with mocking! 🎭
describe('UserService', () => {
  let service: UserService;
  let prisma: PrismaService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: PrismaService,
          useValue: {
            user: {
              findMany: jest.fn().mockResolvedValue([
                { id: '1', name: 'Alex', email: 'alex@test.com' }
              ]),
            },
          },
        },
      ],
    }).compile();

    service = module.get(UserService);
  });

  it('should return all users', async () => {
    const users = await service.findAll();
    expect(users).toHaveLength(1);
    expect(users[0].name).toBe('Alex');
  });
});

// 3. E2E test — test the whole API 🌐
describe('POST /auth/login', () => {
  it('should return a JWT token', () => {
    return request(app.getHttpServer())
      .post('/auth/login')
      .send({ email: 'alex@test.com', password: 'pass123' })
      .expect(200)
      .expect(res => {
        expect(res.body.access_token).toBeDefined();
      });
  });
});

Line-by-line walkthrough

  1. 1. 1. Unit test — test a function in isolation 🔬
  2. 2. Grouping related tests together
  3. 3. Defining an individual test case
  4. 4. Asserting an expected result
  5. 5.
  6. 6.
  7. 7. Defining an individual test case
  8. 8. Asserting an expected result
  9. 9.
  10. 10.
  11. 11. Defining an individual test case
  12. 12. Asserting an expected result
  13. 13.
  14. 14.
  15. 15.
  16. 16. 2. NestJS service test — with mocking! 🎭
  17. 17. Grouping related tests together
  18. 18. Declaring a variable
  19. 19. Declaring a variable
  20. 20.
  21. 21. Setup that runs before each test
  22. 22. Declaring a variable
  23. 23.
  24. 24.
  25. 25. Opening block
  26. 26.
  27. 27.
  28. 28.
  29. 29.
  30. 30.
  31. 31.
  32. 32. Closing block
  33. 33. Closing block
  34. 34. Closing block
  35. 35.
  36. 36.
  37. 37.
  38. 38.
  39. 39.
  40. 40.
  41. 41. Defining an individual test case
  42. 42. Declaring a variable
  43. 43. Asserting an expected result
  44. 44. Asserting an expected result
  45. 45.
  46. 46.
  47. 47.
  48. 48. 3. E2E test — test the whole API 🌐
  49. 49. Grouping related tests together
  50. 50. Defining an individual test case
  51. 51. Returning a value
  52. 52. Method chaining on the previous expression
  53. 53. Method chaining on the previous expression
  54. 54. Method chaining on the previous expression
  55. 55. Method chaining on the previous expression
  56. 56. Asserting an expected result
  57. 57.
  58. 58.
  59. 59.

Spot the bug

describe('add', () => {
  it('should add two numbers', () => {
    expect(add(2, 3)).toBe("5");
  });
});
Need a hint?
Look at the expected value - is it the right type?
Show answer
The test expects the string '5' but add(2,3) returns the number 5. toBe uses strict equality (===), so 5 !== '5'. Fix: change toBe('5') to toBe(5).

Explain like I'm 5

Testing is like a fire drill at school. You don't wait for a real fire to check if exits work! You practice first. Testing your code is the same - you check everything works before real people use it. Better to find problems during practice!

Fun fact

The Mars Climate Orbiter crashed in 1999 because one team used metric units and another used imperial — a bug that testing would have caught. That $125 million mistake could have been prevented by a $5 test! 🚀💥

Hands-on challenge

Write a test for a function that converts Celsius to Fahrenheit. Test: 0°C = 32°F, 100°C = 212°F, -40°C = -40°F (yes, they're the same at -40!).

More resources

Open interactive version (quiz + challenge) ← Back to course: Full-Stack Playbook