semi-automatic mock generation in TypeScript (uses Jest as testing framework)
import {clearMocks, Mock, mockClass, initMocks} from "mockclass"; | |
import {ErrorService} from "../components/http/error.service"; | |
describe('MyService', () => { | |
//use as a decorator in an empty holder class | |
class Mocks extends MockHolder { | |
@Mock(ErrorService) | |
errorService: ErrorService; | |
} | |
const mocks = initMocks("mocks", Mocks); | |
//use as a function | |
const errorServiceMock = mockClass<ErrorService>(ErrorService); | |
let service: MyService; | |
beforeEach(() => { | |
//use only one of these | |
service = new MyService(mocks.errorService); | |
service = new MyService(errorServiceMock); | |
clearMocks(); // mocks are reused between test cases and suites and need to be cleared | |
}); | |
it('should do something', () => { | |
//use only one of these | |
asMock(errorServiceMock.log).mockReturnValue(null); | |
asMock(mocks.errorService.log).mockReturnValue(null); | |
service.doSomething(); | |
//use only one of these | |
expect(errorServiceMock.log).not.toHaveBeenCalled(); | |
expect(mocks.errorService.log).not.toHaveBeenCalled(); | |
}); | |
}); |
import Mock = jest.Mock; | |
const mockedClazzes = []; | |
export function mockClass<C>(clazz: any): C { | |
const keys = describeClass(clazz); | |
const mock = <C>{}; | |
for (const key of keys) { | |
if (key !== "constructor") { | |
try { | |
const name = clazz.name + "." + key; | |
const type = typeof clazz.prototype[key]; | |
if (type && type === 'function') { | |
mock[key] = jest.fn().mockName(name); | |
} else { | |
mock[key] = clazz.prototype[key]; | |
} | |
} catch (e) { | |
} | |
} | |
} | |
mockedClazzes.push(mock); | |
return mock; | |
} | |
export function asMock(func: Function) { | |
return (<Mock>func); | |
} | |
function describeClass(clazz: any): string[] { | |
const proto = clazz.prototype; | |
const props = []; | |
if (proto) { | |
for (let prop in proto) { | |
if (proto.hasOwnProperty(prop)) { | |
props.push(prop); | |
} | |
} | |
} | |
return props; | |
} | |
export function clearMocks() { | |
for (const mockedClazz of mockedClazzes) { | |
for (let prop in mockedClazz) { | |
if (mockedClazz.hasOwnProperty(prop)) { | |
const member = mockedClazz[prop]; | |
if (member.mockClear) { | |
member.mockClear(); | |
} | |
} | |
} | |
} | |
} |
export abstract class MockHolder { | |
name: string; | |
constructor(name: string) { | |
this.name = name; | |
} | |
} | |
export function initMocks<T extends MockHolder>(name: string, constructorFn: new (name: string) => T) { | |
return new constructorFn(name); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment