omermorad / automock

Standalone Library for Automated Mocking of Class Dependencies.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ISC license npm version npm downloads Codecov Coverage ci


Logo

Automock

Standalone Library for Automated Mocking of Class Dependencies.

Rapid and effortless creation of unit tests while ensuring complete isolation of class dependencies.

What is Automock?

Automock is a TypeScript-based library designed for unit testing applications.

By leveraging the TypeScript Reflection API (reflect-metadata) under the hood, Automock simplifies the process of writing tests by automatically generating mock objects for a class's dependencies.

Installation

npm install -D @automock/jest

👷 Upcoming release of Sinon, we are almost there!

🤔 Problem

Consider the following class and interface:

interface Logger {
  log(msg: string, metadata: any): void;
  warn(msg: string, metadata: any): void;
  info(msg: string, metadata: any): void;
}

@Injectable()
class UsersService {
  constructor(private logger: Logger) {}
  
  generateUser(name: string, email: string): User {
    const userData: User = { name, email };
    this.logger.log('returning user data', { user: userData });
    return userData;
  }
}

A sample unit test for this class could resemble the following code snippet:

describe('Users Service Unit Spec', () => {
  let usersService: UsersService; // Unit under test
  let loggerMock: jest.Mocked<Logger>;
  
  beforeAll(() => {
    loggerMock = { log: jest.fn() };
    usersService = new UsersService(loggerMock);
  });
  
  test('call logger log with generated user data', () => {
    usersService.generateUser('Joe', 'joe@due.com');
    expect(loggerMock.log).toBeCalled(); // Verify the call
  });
});

One of the primary challenges in writing unit tests is the requirement to initialize each class dependency individually and generate a mock object for each of these dependencies.

Reveal Even Worse Example 🤦

describe('Users Service Unit Spec', () => {
  let usersService: UsersService;
  let loggerMock: jest.Mocked<Logger>;
  let apiServiceMock: jest.Mocked<ApiService>;

  beforeAll(() => {
    loggerMock = { log: jest.fn(), warn: jest.fn(), info: jest.fn() };
    apiServiceMock = { getUsers: jest.fn(), deleteUser: jest.fn() };
    usersService = new UsersService(loggerMock, apiServiceMock);
  });

  test('...', () => { ... });
});

💡 The Solution

import { TestBed } from '@automock/jest';

describe('Users Service Unit Spec', () => {
  let unitUnderTest: UsersService;
  let logger: jest.Mocked<Logger>;

  beforeAll(() => {
    const { unit, unitRef } = TestBed.create(UsersService).compile();

    // The actual instance of the class
    unitUnderTest = unit;

    // The reference to logger mock object
    logger = unitRef.get(Logger); 
  });

  describe('when something happens', () => {
    test('then expect for something else to happen', async () => {
      await unitUnderTest.callSomeMethod();
      expect(logger.log).toHaveBeenCalled();
    });
  });
});

As demonstrated by the code snippet above, the utilization of Automock enables one to concentrate on testing the logic, rather than expending effort on the laborious task of manually creating mock objects and stub functions. Additionally, there is no need for concern regarding the potential for breaking the class type.

📜 License

Distributed under the MIT License. See LICENSE for more information.

About

Standalone Library for Automated Mocking of Class Dependencies.

License:MIT License


Languages

Language:TypeScript 100.0%