Skip to Content
Course content

70: Unit Testing TypeScript with Jest and ts-jest

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've been relying on manual testing—running the code and seeing if it crashes. That works for small scripts, but in a professional codebase, it's a recipe for disaster. I want to show you how to set up a proper unit testing suite using Jest and ts-jest. Jest is the industry standard for JavaScript testing, but since we're in TypeScript land, we need ts-jest to handle the transpilation on the fly so Jest can actually understand our types.

Getting the environment ready

First, we need the tooling. I usually install these as dev dependencies because your production server doesn't need to know how to run tests. Run this in your terminal:

npm install --save-dev jest ts-jest @types/jest typescript

After that, we need a configuration file. I'm not going to make you write a JSON file by hand; just run npx ts-jest config:init. This creates a jest.config.js that tells Jest, "Hey, whenever you see a .ts file, use ts-jest to process it."

Building a ShoppingCart to test

Let's build something concrete. I'll create a simple ShoppingCart class. It's a classic example because it involves state (the items) and logic (the total calculation), which is exactly what we want to isolate in unit tests.

// shoppingCart.ts
export interface Item {
  name: string;
  price: number;
}

export class ShoppingCart {
  private items: Item[] = [];

  addItem(item: Item) {
    this.items.push(item);
  }

  getTotal(discountCode?: string): number {
    const subtotal = this.items.reduce((sum, item) => sum + item.price, 0);
    if (discountCode === 'SAVE10') {
      return subtotal * 0.9;
    }
    return subtotal;
  }
}


Writing our first test case

Now, let's see if the addItem and getTotal logic actually works. I'll create a file named shoppingCart.test.ts. I like the .test.ts suffix because it makes it incredibly easy to find tests in a large project tree.

// shoppingCart.test.ts
import { ShoppingCart } from './shoppingCart';

test('should calculate the total of added items', () => {
  const cart = new ShoppingCart();
  cart.addItem({ name: 'Keyboard', price: 100 });
  cart.addItem({ name: 'Mouse', price: 50 });
  
  expect(cart.getTotal()).toBe(150);
});

Hitting a wall with configuration

I'll be honest: the first time I ran this, it failed. I got a confusing error: SyntaxError: Unexpected token 'export'. I had forgotten that I'd initialized my project with "type": "module" in package.json, but my Jest config was expecting CommonJS.

I could have spent an hour fighting with ESM modules in Jest (which is a rabbit hole you don't want to go down today). Instead, I realized the quickest fix for this small project was to ensure ts-jest was correctly mapped in my config and that I wasn't mixing module systems. I updated my jest.config.js to explicitly handle the transformation of TS files, and suddenly, the "Unexpected token" disappeared and the tests passed. Always check your package.json and jest.config.js alignment when you see syntax errors in a test runner.

Testing the discount logic and edge cases

Now that the plumbing is working, we can actually test the business logic. A good engineer doesn't just test the "happy path"; they try to break things. Let's add tests for the discount code and what happens when the cart is empty.

test('should apply 10% discount with SAVE10 code', () => {
  const cart = new ShoppingCart();
  cart.addItem({ name: 'Monitor', price: 200 });
  
  expect(cart.getTotal('SAVE10')).toBe(180);
});

test('should return 0 for an empty cart', () => {
  const cart = new ShoppingCart();
  expect(cart.getTotal()).toBe(0);
});

test('should ignore invalid discount codes', () => {
  const cart = new ShoppingCart();
  cart.addItem({ name: 'Cable', price: 20 });
  
  expect(cart.getTotal('WRONGCODE')).toBe(20);
});

Now, when I run npm test, I get a satisfying green checklist. If I accidentally change the discount multiplier to 0.8 in the source code, Jest will immediately tell me exactly which test failed and what the expected value was versus what it actually got.




📋 Practical Task

Exercise: Implementing and Testing a Tax Calculator

You need to extend the ShoppingCart logic to handle sales tax, but you must ensure it doesn't break the existing functionality. Follow these requirements:

  • Modify the getTotal method in shoppingCart.ts to accept an optional taxRate (a decimal, e.g., 0.07 for 7%).
  • The tax should be applied to the discounted price (calculate the discount first, then add the tax).
  • Create a new test file taxCalculator.test.ts.
  • Write three tests:
    1. One where a 10% tax is applied to a standard total.
    2. One where both the 'SAVE10' discount and a tax rate are applied simultaneously.
    3. One where the tax rate is 0, ensuring the total remains unchanged.

Run your tests using npm test to verify that your tax logic is mathematically sound and that your types are correctly enforced.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.