Labs ICT
Pro Login

Testing Objects & Classes

Unit testing OOP code.

Testing Objects and Classes

Classes and objects are everywhere in JavaScript. Testing them is not much different from testing functions, but there are a few patterns that make it cleaner. Let me show you what I mean.

The key is testing the public interface, not the internal implementation. You care about what the object does, not how it does it.

Testing Class Methods

Create an instance and call its methods. Assert the results.


class BankAccount {
  constructor(balance = 0) {
    this.balance = balance;
  }

  deposit(amount) {
    if (amount <= 0) throw new Error('Amount must be positive');
    this.balance += amount;
    return this.balance;
  }

  withdraw(amount) {
    if (amount > this.balance) throw new Error('Insufficient funds');
    this.balance -= amount;
    return this.balance;
  }
}

describe('BankAccount', () => {
  it('starts with given balance', () => {
    const account = new BankAccount(100);
    expect(account.balance).toBe(100);
  });

  it('deposits money', () => {
    const account = new BankAccount(0);
    account.deposit(50);
    expect(account.balance).toBe(50);
  });

  it('withdraws money', () => {
    const account = new BankAccount(100);
    account.withdraw(30);
    expect(account.balance).toBe(70);
  });

  it('throws when withdrawing too much', () => {
    const account = new BankAccount(10);
    expect(() => account.withdraw(20)).toThrow('Insufficient funds');
  });
});
    

Testing State Changes

Objects often change state. Test that each method updates the state correctly.


class Counter {
  constructor() {
    this.count = 0;
  }

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }

  reset() {
    this.count = 0;
  }
}

it('tracks state changes', () => {
  const counter = new Counter();
  counter.increment();
  counter.increment();
  expect(counter.count).toBe(2);
  counter.decrement();
  expect(counter.count).toBe(1);
  counter.reset();
  expect(counter.count).toBe(0);
});
    

Testing Return Values from Methods

Some methods return values. Test those too.


class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  getProfile() {
    return {
      displayName: this.name,
      contactInfo: this.email
    };
  }
}

it('returns correct profile', () => {
  const user = new User('Alice', 'alice@example.com');
  expect(user.getProfile()).toEqual({
    displayName: 'Alice',
    contactInfo: 'alice@example.com'
  });
});
    
Try it Yourself →

Key Takeaways

  • Test the public interface, not internal implementation
  • Create instances in tests and call methods directly
  • Test both return values and state changes
  • Test error cases like invalid state transitions
  • Keep tests focused on one behavior per test case