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.