Create mocked service (object) with one method returning a value - jestjs

In an Angular environment, how can I very easily create in a Jest environment a mocked service for a service object returning a specific value? This could be via Jest of ng-mocks, etc.
An oversimplified example:
// beforeEach:
// setup an Angular component with a service component myMockService
// Test 1:
// fake "myMockService.doSomething" to return value 10
// expect(myComponent.getBalance()).toEqual( "Your balance: 10");
// Test 2:
// fake "myMockService.doSomething" to return value 20
// expect(myComponent.getBalance()).toEqual( "Your balance: 20");
I have studied the Jest and ng-mocks docs but didn't find a very easy approach. Below you find 2 working approaches. Can you improve the version?
My simplified Angular component:
#Component({
selector: 'app-servicecounter',
templateUrl: './servicecounter.component.html'
})
export class ServicecounterComponent {
private myValue: number = 1;
constructor(private counterService: CounterService) { }
public doSomething(): void {
// ...
myValue = this.counterService.getCount();
}
}
This is the simplified service:
#Injectable()
export class CounterService {
private count = 0;
constructor() { }
public getCount(): Observable<number> {
return this.count;
}
public increment(): void {
this.count++;
}
public decrement(): void {
this.count--;
}
public reset(newCount: number): void {
this.count = newCount;
}
}
Try 1: a working solution: with 'jest.genMockFromModule'.
The disadvantage is that I can only create a returnValue only at the start of each series of tests, so at beforeEach setup time.
beforeEach(async () => {
mockCounterService = jest.genMockFromModule( './counterservice.service');
mockCounterService.getCount = jest.fn( () => 3);
mockCounterService.reset = jest.fn(); // it was called, I had to mock.fn it.
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [ { provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
fixture = TestBed.createComponent(ServicecounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('shows the count', () => {
setFieldValue(fixture, 'reset-input', String(currentCount));
click(fixture, 'reset-button');
expect(mockCounterService.getCount()).toEqual( 3);
expect( mockCounterService.getCount).toBeCalled();
});
Try 2: replace 'jest.genMockFromModule' with 'jest.createMockFromModule': works equally well.
The disadvantage is still that I can create a returnValue only at the start of each series of tests, so at beforeEach setup time.
Try 3: create a mock object upfront: didn't work
jest.mock( "./counterservice.service");
beforeEach(async () => {
// Create fake
mockCounterService = new CounterService();
(mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [{ provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
fixture = TestBed.createComponent(ServicecounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('shows the count', () => {
// do something that will trigger the mockCountService getCount method.
expect(mockCounterService.getCount).toEqual( 0);
});
This doesn't work, giving the error:
> (mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
> Cannot read property 'mockReturnValue' of undefined
Try 4: with .fn(). The disadvantage is that the original class may change, then the test object MUST change.
beforeEach(async () => {
mockCounterService = {
getCount: jest.fn().mockReturnValue( 0),
increment: jest.fn,
decrement: jest.fn(),
reset: jest.fn
};
await TestBed.configureTestingModule({
declarations: [ServicecounterComponent],
providers: [{ provide: CounterService, useValue: mockCounterService }],
}).compileComponents();
});
it( '... ', () => {
// ...
expect(mockCounterService.reset).toHaveBeenCalled();
});
This time, the error is:
> Matcher error: received value must be a mock or spy function ...
> expect(mockCounterService.reset).toHaveBeenCalled();
Can you help improving this way of working?

You need to use MockBuilder to mock the service, and MockInstance to customize it.
Also getCount is an observable, therefore its mock should return Subject, which we can manipulate.
// to reset MockInstance customizations after tests
MockInstance.scope();
// to use jest.fn on all mocks https://ng-mocks.sudo.eu/extra/auto-spy
beforeEach(() => ngMocks.autoSpy('jest'));
afterEach(() => ngMocks.autoSpy('reset'));
beforeEach(() => MockBuilder(ServicecounterComponent, CounterService));
it('testing', () => {
// this is our control of observable of getCount
const getCount$ = new Subject<number>();
// now we need to return it when getCount is called
const getCount = MockInstance(CounterService, 'getCount', jest.fn())
.mockReturnValue(getCount$);
// now we can use it in our test.
const fixture = MockRender(ServicecounterComponent);
ngMocks.click('.reset-button');
expect(getCount).toHaveBeenCalled();
getCount$.next(3);
expect(ngMocks.formatText(fixture)).toContain('3');
});

Related

Nest Js Jest Unit Testing: Providing a mock implementation not used from controller

I have already a mock class implemented as a part of my previous work, which I want to provide as a part of my Jest Unit Testing.
Let me explain in code, that's better:
My Controller code:
export class VpcController {
constructor(private readonly vpcService: VpcService) {}
#Get
list() {
return this.vpcService.list();
}
}
My controller Jest Unit Test:
class VpcServiceMockFactory {
private list() {
return jest.fn().mockResolvedValue([TEMPLATE_VPC]);
}
getMock() {
const repoService: RepositoryService = new RepositoryService();
const vpsServiceMock: VpcServiceMock = new VpcServiceMock(repoService);
vpsServiceMock.create(TEMPLATE_VPC.name);
// This works
// return {
// list: this.list(),
// }
// This does not work
return {
list: vpsServiceMock.list(),
get: vpsServiceMock.get(TEMPLATE_VPC.id),
// create: vpsServiceMock.create('new-vpc'),
// update: vpsServiceMock.update(TEMPLATE_VPC.id, 'updated-name'),
// delete: vpsServiceMock.delete(TEMPLATE_VPC.id),
}
}
}
describe('VpcControllerTest', () => {
let controller: VpcController;
let spyService: VpcService;
beforeEach(async () => {
// Mock Services
const MockVpcServiceProvider = {
provide: VpcService,
useFactory: () => new VpcServiceMockFactory().getMock()
}
// Class-unter-test instantiation
const module: TestingModule = await Test.createTestingModule({
controllers: [VpcController],
providers: [VpcService, MockVpcServiceProvider],
}).compile()
// Get the instance handlers
controller = module.get<VpcController>(VpcController);
spyService = module.get<VpcService>(VpcService);
});
it('Get collection of VPCs', async () => {
// Execute the method
const result = await controller.list();
// Assertion
expect(spyService.list).toHaveBeenCalled();
expect(result.length).toBe(1);
expect(result[0].name).toBe('zopa');
});
}
My VpcServiceMock class:
export class VpcServiceMock {
constructor(private repository: RepositoryService) {}
list() {
return this.repository.list<VpcModel>(VPC);
}
}
My RepositoryService class:
async list<T>(type: VPC): Promise<T[]> {
return <T[]>this.aSimpleJsonObject[type];
}
However when I am running it, it is showing this error:
● VpcControllerTest › Test-2: Get collection of VPCs
TypeError: this.vpcService.list is not a function
38 | #ApiForbiddenResponse({ description: 'Unauthorized Request' })
39 | list() {
> 40 | return this.vpcService.list();
| ^
41 | }
42 |
43 | #Get(':id')
at VpcController.list (src/vpc/vpc.controller.ts:40:28)
at Object.<anonymous> (src/vpc/vpc.controller.spec.ts:87:37)
So the only way I can make it work:
If I provide a mock implementation in the Jest Unit test's VpcServiceMockFactory class (like I showed in the commented out code there as // This works).
Definitely I am missing something here, which I am not able to figure out.
Got my issue.
Basically I missed the jest.fn().mockResolvedValue.
Modified to make it work:
class VpcServiceMockFactory {
async getMock() {
const repoService: RepositoryService = new RepositoryService();
const vpcServiceMock: VpcServiceMock = new VpcServiceMock(repoService);
await vpcServiceMock.create(TEMPLATE_VPC.name); // repository store initialization
return {
list: jest.fn().mockResolvedValue(vpcServiceMock.list()),
get: jest.fn().mockResolvedValue(vpcServiceMock.get(TEMPLATE_VPC.id)),
create: jest.fn().mockResolvedValue(vpcServiceMock.create('new-vpc')),
update: jest.fn().mockResolvedValue(vpcServiceMock.update(TEMPLATE_VPC.id, 'updated-name')),
delete: jest.fn().mockResolvedValue(vpcServiceMock.delete(TEMPLATE_VPC.id)),
}
}
}

How would I mock the DataSource from TypeORM in NestJS Jest tests?

I am trying to write tests for a small project in NestJS. Here is the relevant code for context:
dummy.controller.ts
#Controller(UrlConstants.BASE_URL + 'dummy')
export class DummyContoller {
constructor(
private readonly sessionService: SessionService,
) { }
#Get('validateSession')
async checkValidateSession(#Query('sessionId') sessionId: string) {
const session = await this.sessionService.validateSession(sessionId);
console.log(session);
return { message: "OK" };
}
}
session.service.ts
#Injectable()
export class SessionService {
constructor(
private readonly sessionRepo: SessionRepository,
private readonly accountRepo: AccountRepository
) { }
#WithErrorBoundary(AuthCodes.UNKNOWN_LOGIN_ERROR)
async validateSession(sessionId: string) {
const session = await this.sessionRepo.findOneBy({ sessionId });
if (!session || this.isSessionExpired(session)) {
session && await this.sessionRepo.remove(session);
throw new HttpException({
code: AuthCodes.SESSION_TIMEOUT,
message: AuthMessages.SESSION_TIMEOUT
}, HttpStatus.UNAUTHORIZED)
}
return session;
}
...
}
session.repository.ts (Any repository)
#Injectable()
export class SessionRepository extends Repository<Session> {
constructor(private dataSource: DataSource) {
super(Session, dataSource.createEntityManager())
}
...
}
This is how I wrote my test (this is my first time writing a test using Jest and I am not really experienced in writing tests in general):
describe('DummyController', () => {
let dummyContoller: DummyContoller;
let sessionService: SessionService;
let sessionRepo: SessionRepository;
let accountRepo: AccountRepository;
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [DummyContoller],
providers: [SessionService, SessionRepository, AccountRepository]
}).compile();
dummyContoller = module.get<DummyContoller>(DummyContoller);
sessionService = module.get<SessionService>(SessionService);
sessionRepo = module.get<SessionRepository>(SessionRepository);
accountRepo = module.get<AccountRepository>(AccountRepository);
})
describe('checkValidateSession', () => {
it('should return valid session', async () => {
const sessionId = "sessionId1";
const session = new Session();
jest.spyOn(sessionService, 'validateSession').mockImplementation(async (sessionId) => session);
expect(await dummyContoller.checkValidateSession(sessionId)).toBe(session);
})
})
})
Upon running the test, I encounter:
Nest can't resolve dependencies of the SessionRepository (?). Please make sure that the argument DataSource at index [0] is available in the RootTestModule context.
Potential solutions:
- If DataSource is a provider, is it part of the current RootTestModule?
- If DataSource is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing DataSource */ ]
})
I looked this problem and I came across a number of solutions but most of them had #InjectRepository() instead of creating a separate Repository class where they would provide getRepositoryToken() and then use a mock factory [Link]. I couldn't find a way to make this work.
Another solution suggested using an in-memory database solution [Link]. But this felt more like a hack rather than a solution.
How can I test the above setup?
Based on this comment, I was able to get this working by using the following in the providers in the test:
providers: [
SessionService,
{ provide: SessionRepository, useClass: SessionMockRepository },
]
SessionMockRepository contains a mocked version of all additional functions in that particular repository:
export class SessionMockRepository extends Repository<Session> {
someFunction = async () => jest.fn();
}
Currently, this works for me so I am accepting this. I am still open to more answers if there is a better way to do this.

Jest .toHaveBeenCalled() not recording function calls

I'm testing a service in my NestJs application which calls a factory to receive an object with a method up(), which I'm mocking like this:
export const mockExecutorFactory = {
getExecutor: jest.fn().mockImplementation(() => {
return {
up: jest.fn().mockImplementation((_updatedInstance, _parameters) => {
console.log('****************************');
return Promise.resolve({ successful: true });
}),
};
}),
};
In my describe block for the service test I initialize the executor like this:
let executor;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
InstanceService,
{ provide: getRepositoryToken(Instance), useValue: mockInstanceRepository },
ExecutorFactory,
],
})
.overrideProvider(ExecutorFactory)
.useValue(mockExecutorFactory)
.compile();
service = module.get<InstanceService>(InstanceService);
executor = module.get<ExecutorFactory>(ExecutorFactory).getExecutor();
});
And the test its self is written like this:
it('should create a new instance with default settings', async () => {
// Check if instance created correctly
expect(
await service.createInstance(
MOCK_INSTANCE_CREATE_PARAMS.solutionId,
MOCK_INSTANCE_CREATE_PARAMS.instanceType,
MOCK_INSTANCE_CREATE_PARAMS.orgId,
MOCK_INSTANCE_CREATE_PARAMS.parameters,
MOCK_INSTANCE_CREATE_PARAMS.customPrice,
true,
MOCK_INSTANCE_CREATE_PARAMS.req,
),
).toEqual({ instance: MOCK_INSTANCE_DEFAULT });
console.log(executor);
expect(executor.up).toHaveBeenCalled();
});
Essentially, the createInstance() runs through some logic and at the end is supposed to call the executor's up() function.
From the terminal output (photo below) I can see by the console logs that the the up() is being called, but the test fails... Any idea why?

Jest with NestJS and async function

I'm trying to a test a async function of a service in nestJS.
this function is async... basically get a value (JSON) from database (using repository - TypeORM), and when successfully get the data, "transform" to a different class (DTO)...
the implementation:
async getAppConfig(): Promise<ConfigAppDto> {
return this.configRepository.findOne({
key: Equal("APPLICATION"),
}).then(config => {
if (config == null) {
return new class implements ConfigAppDto {
clientId = '';
clientSecret = '';
};
}
return JSON.parse(config.value) as ConfigAppDto;
});
}
using a controller, I checked that this worked ok.
Now, I'm trying to use Jest to do the tests, but with no success...
My problem is how to mock the findOne function from repository..
Edit: I'm trying to use #golevelup/nestjs-testing to mock Repository!
I already mocked the repository, but for some reason, the resolve is never called..
describe('getAppConfig', () => {
const repo = createMock<Repository<Config>>();
beforeEach(async () => {
await Test.createTestingModule({
providers: [
ConfigService,
{
provide: getRepositoryToken(Config),
useValue: repo,
}
],
}).compile();
});
it('should return ConfigApp parameters', async () => {
const mockedConfig = new Config('APPLICATION', '{"clientId": "foo","clientSecret": "bar"}');
repo.findOne.mockResolvedValue(mockedConfig);
expect(await repo.findOne()).toEqual(mockedConfig); // ok
const expectedReturn = new class implements ConfigAppDto {
clientId = 'foo';
clientSecret = 'bar';
};
expect(await service.getAppConfig()).toEqual(expectedReturn);
// jest documentation about async -> https://jestjs.io/docs/en/asynchronous
// return expect(service.getAppConfig()).resolves.toBe(expectedReturn);
});
})
the expect(await repo.findOne()).toEqual(mockedConfig); works great;
expect(await service.getAppConfig()).toEqual(expectedReturn); got a timeout => Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout;
using debug, I see that the service.getAppConfig() is called, the repository.findOne() too, but the .then of repository of findOne is never called.
Update: I'm trying to mock the repository using #golevelup/nestjs-testing, and for some reason, the mocked result don't works on service.
If I mock the repository using only jest (like code below), the test works... so, I think my real problem it's #golevelup/nestjs-testing.
...
provide: getRepositoryToken(Config),
useValue: {
find: jest.fn().mockResolvedValue([new Config()])
},
...
So, my real problem is how I'm mocking the Repository on NestJS.
For some reason, when I mock using the #golevelup/nestjs-testing, weird things happens!
I really don't found a good documentation about this on #golevelup/nestjs-testing, so, I gave up using it.
My solution for the question was to use only Jest and NestJS functions... the result code was:
Service:
// i'm injecting Connection because I need for some transactions later;
constructor(#InjectRepository(Config) private readonly configRepo: Repository<Config>, private connection: Connection) {}
async getAppConfig(): Promise<ConfigApp> {
return this.configRepo.findOne({
key: Equal("APPLICATION"),
}).then(config => {
if (config == null) {
return new ConfigApp();
}
return JSON.parse(config.value) as ConfigApp;
})
}
Test:
describe('getAppConfig', () => {
const configApi = new Config();
configApi.key = 'APPLICATION';
configApi.value = '{"clientId": "foo", "clientSecret": "bar"}';
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
ConfigAppService,
{
provide: getRepositoryToken(Config),
useValue: {
findOne: jest.fn().mockResolvedValue(new
Config("APPLICATION", '{"clientId": "foo", "clientSecret": "bar"}')),
},
},
{
provide: getConnectionToken(),
useValue: {},
}
],
}).compile();
service = module.get<ConfigAppService>(ConfigAppService);
});
it('should return ConfigApp parameters', async () => {
const expectedValue: ConfigApp = new ConfigApp("foo", "bar");
return service.getAppConfig().then(value => {
expect(value).toEqual(expectedValue);
})
});
})
some sources utilized for this solution:
https://github.com/jmcdo29/testing-nestjs/tree/master/apps/typeorm-sample
I think expect(await repo.findOne()).toEqual(mockedConfig); works because you mocked it, so it returns right away.
In the case of expect(await service.getAppConfig()).toEqual(expectedReturn);, you did not mock it so it is probably taking more time, thus the it function returns before the Promise resolved completely.
The comments you posted from jest documentation should do the trick if you mock the call to getAppConfig().
service.getAppConfig = jest.fn(() => Promise.resolve(someFakeValue))
or
spyOn(service, 'getAppConfig').and.mockReturnValue(Promise.resolve(fakeValue))
This answer from #roberto-correia made me wonder if there must be something wrong with the way we are using createMock from the package #golevelup/nestjs-testing.
It turns out that the reason why the method exceeds the execution time has to do with the fact that createMock does not implement the mocking, and does not return anything, unless told to do so.
To make the method work, we have to make the mocked methods resolve something at the beginning of the test:
usersRepository.findOneOrFail.mockResolvedValue({ userId: 1, email: "some-random-email#email.com" });
A basic working solution:
describe("UsersService", () => {
let usersService: UsersService;
const usersRepository = createMock<Repository<User>>();
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: usersRepository,
},
}).compile();
usersService = module.get(UsersService);
});
it("should be defined", () => {
expect(usersService).toBeDefined();
});
it("finds a user", async () => {
usersRepository.findOne.mockResolvedValue({ userId: 1, email: "some-random-email#email.com" });
expect(await usersRepository.findOne()).toBe({ userId: 1, email: "some-random-email#email.com" });
});
});

How to jest.spyOn only the base class method, not the overridden method

Trying to write test scripts for my nestjs application.
I have controller/service framework, that looks like this:
Controller:
export class MyController {
constructor(
protected _svc: MyService
) {}
#Get()
async getAll(): Promise<Array<Person>> {
return await this._svc.findAll();
}
}
Service:
#Injectable()
export class MyService extends DbService < Person > {
constructor(
private _cache: CacheService
) {
super(...);
}
async findAll() {
return super.findAll().then(res => {
res.map(s => {
this._cache.setValue(`key${s.ref}`, s);
});
return res;
});
}
Base class:
#Injectable()
export abstract class DbService<T> {
constructor() {}
async findAll(): Promise<Array<T>> {
...
}
}
My controller is the entry point when calling an endpoint on the API. This calls the service, which extends the DbService, which is what communicates with my database. There are a lot of services which all extend this DbService. In this case, the MyService class overrides the DbService "findAll" method to do some cache manipulation.
My test script has this:
let myController: MyController;
let myService: MyService;
describe("MyController", async () => {
let spy_findall, spy_cacheset;
beforeAll(() => {
this._cacheService = {
// getValue, setValue, delete methods
};
myService = new MyService(this._cacheService);
myController = new MyController(myService);
spy_findall = jest.spyOn(myService, "findAll").mockImplementation(async () => {
return [testPerson];
});
spy_cacheset = jest.spyOn(this._cacheService, "setValue");
});
beforeEach(async () => {
jest.clearAllMocks();
});
describe("getAll", () => {
it("should return an array of one person", async () => {
await myController.getAll().then(r => {
expect(r).toHaveLength(1);
expect(spy_findall).toBeCalledTimes(1);
expect(spy_cacheset).toBeCalledTimes(1);
expect(r).toEqual([testPerson]);
});
});
});
});
Now, obviously the mockImplementation of findAll mocks the "findAll" on MyService, so the test fails because spy_cacheset is never called.
What I would like to do is mock only the base method "findAll" from DbService, so that I maintain the extra functionality that exists in MyService.
Is there a way of doing this without just renaming the methods in MyService, which I would rather avoid doing?
Edited to add:
Thanks to #Jonatan lenco for such a comprehensive reponse, which I have taken on board and implemented.
I have one further question. CacheService, DbService and a whole lot of other stuff (some of which I want to mock, other that I don't) is in an external library project, "shared".
cache.service.ts
export class CacheService {...}
index.ts
export * from "./shared/cache.service"
export * from "./shared/db.service"
export * from "./shared/other.stuff"
....
This is then compiled and included as a package in node_modules.
In the project where I am writing the tests:
import { CacheService, DocumentService, OtherStuff } from "shared";
Can I still use jest.mock() for just the CacheService, without mocking the whole "shared" project?
In this case since you want to spy on an abstract class (DbService), you can spy on the prototype method:
jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
return [testPerson];
});
Also here some recommendations for your unit tests with NestJS and Jest:
Use jest.mock() in order to simplify your mocking (in this case for CacheService). See https://jestjs.io/docs/en/es6-class-mocks#automatic-mock.
When you do jest.spyOn(), you can assert the method execution without the need of the spy object. Instead of:
spy_findall = jest.spyOn(myService, "findAll").mockImplementation(async () => {
return [testPerson];
});
...
expect(spy_findall).toBeCalledTimes(1);
You can do:
jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
return [testPerson];
});
...
expect(DbService.prototype.findAll).toBeCalledTimes(1);
If you are mocking a class properly, you do not need to spy on the method (if you do not want to mock its implementation).
Use the Testing utilities from NestJS, it will help you a lot specially when you have complex dependency injection. See https://docs.nestjs.com/fundamentals/testing#testing-utilities.
Here is an example that applies these 4 recommendations for your unit test:
import { Test } from '#nestjs/testing';
import { CacheService } from './cache.service';
import { DbService } from './db.service';
import { MyController } from './my.controller';
import { MyService } from './my.service';
import { Person } from './person';
jest.mock('./cache.service');
describe('MyController', async () => {
let myController: MyController;
let myService: MyService;
let cacheService: CacheService;
const testPerson = new Person();
beforeAll(async () => {
const module = await Test.createTestingModule({
controllers: [MyController],
providers: [
MyService,
CacheService,
],
}).compile();
myService = module.get<MyService>(MyService);
cacheService = module.get<CacheService>(CacheService);
myController = module.get<MyController>(MyController);
jest.spyOn(DbService.prototype, 'findAll').mockImplementation(async () => {
return [testPerson];
});
});
beforeEach(async () => {
jest.clearAllMocks();
});
describe('getAll', () => {
it('Should return an array of one person', async () => {
const r = await myController.getAll();
expect(r).toHaveLength(1);
expect(DbService.prototype.findAll).toBeCalledTimes(1);
expect(cacheService.setValue).toBeCalledTimes(1);
expect(r).toEqual([testPerson]);
});
});
});
NOTE: for the testing utilities to work and also for your application to work well, you will need to add the #Controller decorator on the class MyController:
import { Controller, Get } from '#nestjs/common';
...
#Controller()
export class MyController {
...
}
About mocking specific items of another package (instead of mocking the whole package) you could do this:
Create a class in your spec file (or you can create it in another file that you import, or even in your shared module) which has a different name but has the same public method names. Note that we use jest.fn() since we do not need to provide an implementation, and that already spies in the method (no need to later do jest.spyOn() unless you have to mock the implementation).
class CacheServiceMock {
setValue = jest.fn();
}
When setting up the providers of your testing module, tell it that you are "providing" the original class but actually providing the mocked one:
const module = await Test.createTestingModule({
controllers: [MyController],
providers: [
MyService,
{ provide: CacheService, useClass: CacheServiceMock },
],
}).compile();
For more info about providers see https://angular.io/guide/dependency-injection-providers (Nest follows the same idea of Angular).

Resources