redis mock for testing in nestjs - unit-testing

I am writing test cases in my email-service.spec.ts file
my email-service file
#Injectable()
export class EmailSubscriptionService {
private nodeTokenCache;
private result;
constructor(#InjectRepository(ConsumerEmailSubscriptions) private readonly emailSubscriptions: Repository<ConsumerEmailSubscriptions>,
#InjectRepository(EmailSubscriptions) private readonly emailSubscriptionLegacy: Repository<EmailSubscriptions>,
#InjectRedisClient('0') private redisClient: Redis.Redis,
private readonly config: ConfigService, private http: HttpService,
private readonly manageSavedSearchService: ManageSavedSearchService) {
}
my email-service.spec.ts file
import { RedisService } from 'nestjs-redis';
import { ConfigService } from '#nestjs/config';
import { HttpService } from '#nestjs/common';
import { ManageSavedSearchService } from './../manage-saved-search/manage-saved-search.service';
describe('EmailSubscriptionService', () => {
let service: EmailSubscriptionService;
let entity : ConsumerEmailSubscriptions;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports:[RedisModule],
// https://github.com/nestjs/nest/issues/1229
providers: [EmailSubscriptionService,
{
// how you provide the injection token in a test instance
provide: getRepositoryToken(ConsumerEmailSubscriptions),
// as a class value, Repository needs no generics
useClass: Repository,
// useValue: {
// }
},
{
provide: getRepositoryToken(EmailSubscriptions),
useClass: Repository,
},
RedisService,
// {
// provide : RedisService,
// useClass: Redis
// },
ConfigService, HttpService, ManageSavedSearchService
],
}).compile();
service = module.get<EmailSubscriptionService>(EmailSubscriptionService);
// entity = module.get<Repository<ConsumerEmailSubscriptions>>(getRepositoryToken(ConsumerEmailSubscriptions));
});
it('should be defined', async () => {
expect(service).toBeDefined;
});
});
result ---->
Nest can't resolve dependencies of the EmailSubscriptionService (ConsumerEmailSubscriptionsRepository, EmailSubscriptionsRepository, ?, ConfigService, HttpService, ManageSavedSearchService). Please make sure that the argument REDIS_CLIENT_PROVIDER_0 at index [2] is available in the RootTestModule context.
I am unable to mock my redisclient in email-service.spec.ts as per the dependency in the service file. I have tried useClass, added RedisService in provide and there are no get-redis methods.
I am able to mock the repositories and for services, I don't know for sure as I am stuck with redis.
Any idea how to mock redis, couldn't find anything in the docs. Also in the next step, will importing the services work or I have to do anything else?
ConfigService, HttpService, manageSavedSearchService: ManageSavedSearchService

If you want to mock the RedisClient in your tests you need to provide the same DI token as what you get back from #InjectRedisClient('0'). This will allow you to replace the redis client for the purposes of your test.
I'm not familiar with the specific Redis library you're using but assuming that it's this one you can take a look at how the token is constructed

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EmailSubscriptionService,{
provide: EmailSubscriptionService,
useValue: {
getClient: jest.fn(),
}
}],
}).compile();
service = module.get<EmailSubscriptionService>(EmailSubscriptionService);
});
this seems to work somehow, the service class constructor uses many other classes, but using it in provide works..it is kind of counter intuitive as the classes in constructor need to be mocked individually, but without doing that it works.

Related

How to provide Injected constant to service in nestjs when unit testing

I try to create unit tests for my small application. I want to test a service that uses injected configurations and other services.
#Injectable()
export class AuthService {
private readonly localUri: string;
constructor(
#Inject(CORE_CONFIG_TOKEN) private readonly coreConfig: ICoreConfig,
#Inject(PROVIDER_CONFIG_TOKEN) private readonly providerConfig: IProviderConfig,
private readonly _httpService: HttpService,
private readonly _usersService: UsersService,
) {
this.localUri = `http://${this.coreConfig.domain}:${this.coreConfig.port}`;
}
...
/**
* Checks if a given email is already taken
* #param email
*/
async isEmailTaken(email: string): Promise<boolean> {
return (await this._usersService.findUserByEmail(email)) !== undefined;
}
...
I do not understand how to test this service. I don't know how to provide a correct TestModule provider for the injected configuration #Inject(CORE_CONFIG_TOKEN) private readonly coreConfig: ICoreConfig
const testCoreConfig = '{...}'
const module = await Test.createTestingModule({
providers: [AuthService, {
provide: 'CORE_CONFIG_TOKEN',
useClass: testCoreConfig ,
}],
}).compile();
Also I am not sure if I would need also create the other imported services. I just would like to check if they are called. And if so return mock data. This I can do but I am stuck with the module setup.
All samples I found so far where just services with one repository. And more or less checks if the service exists. But no checks against the logic of the implementation and the connections between the classes.
I hope my question is clear
Thank you
I think you should be using the useValue property instead of useClass if you're passing an object of key values?
providers: [
{
provide: CORE_CONFIG_TOKEN,
useValue: {
domain: 'nestjs.com',
port: 80,
},
},
],
There's also some documentation on creating a config provider/service on nestjs for your modules.
I've also created a nestjs-config module you can use similar to this.
#Injectable()
class TestProvider {
constructor(private readonly #InjectConfig() config) {
this.localUri = config.get('local.uri');
}
#Module({
imports: [ConfigModule.load('path/to/config/file/*.ts')],
providers: [TestProvider],
})
export AppModule {}
//config file 'config/local.ts'
export default {
uri: `https://${process.env.DOMAIN}:/${process.env.PORT}`,
}

How to test Angular2's router.navigate?

I've run into missing <router-outlet> messages in other unit tests, but just to have a nice isolated example, I created an AuthGuard that checks if a user is logged in for certain actions.
This is the code:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (!this.authService.isLoggedIn()) {
this.router.navigate(['/login']);
return false;
}
return true;
}
Now I want to write a unit test for this.
This is how I start my test:
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{
path: 'login',
component: DummyComponent
}
])
],
declarations: [
DummyComponent
],
providers: [
AuthGuardService,
{
provide: AuthService,
useClass: MockAuthService
}
]
});
});
I created a DummyComponent that does nothing.
Now my test. Pretend that the service returns false and that it triggers this.router.navigate(['/login']):
it('should not let users pass when not logged in', (): void => {
expect(authGuardService.canActivate(<any>{}, <any>{})).toBe(false);
});
This will throw an exception with "Cannot find primary outlet to load".
Obviously I can use toThrow() instead of toBe(false), but that doesn't seem like a very sensible solution. Since I'm testing a service here, there is no template where I can put the <router-outlet> tag. I could mock the router and make my own navigate function, but then what's the point of RouterTestingModule? Perhaps you even want to check that navigation worked.
I could mock the router and make my own navigate function, but then what's the point of RouterTestingModule? Perhaps you even want to check that navigation worked.
There's no real point. If his is just a unit test for the auth guard, then just mock and spy on the mock to check that it's navigate method was called with the login argument
let router = {
navigate: jasmine.createSpy('navigate')
}
{ provide: Router, useValue: router }
expect(authGuardService.canActivate(<any>{}, <any>{})).toBe(false);
expect(router.navigate).toHaveBeenCalledWith(['/login']);
This is how unit tests should normally be written. To try to test any actual real navigation, that would probably fall under the umbrella of end-to-end testing.
If you want to test the router without mocking it you can just inject it into your test and then spy directly on the navigate method there. The .and.stub() will make it so the call doesn't do anything.
describe('something that navigates', () => {
it('should navigate', inject([Router], (router: Router) => {
spyOn(router, 'navigate').and.stub();
expect(authGuardService.canActivate(<any>{}, <any>{})).toBe(false);
expect(router.navigate).toHaveBeenCalledWith(['/login']);
}));
});
this worked for me
describe('navigateExample', () => {
it('navigate Example', () => {
const routerstub: Router = TestBed.get(Router);
spyOn(routerstub, 'navigate');
component.navigateExample();
});
});
it(`editTemplate() should navigate to template build module with query params`, inject(
[Router],
(router: Router) => {
let id = 25;
spyOn(router, "navigate").and.stub();
router.navigate(["/template-builder"], {
queryParams: { templateId: id }
});
expect(router.navigate).toHaveBeenCalledWith(["/template-builder"], {
queryParams: { templateId: id }
});
}
));
I came up with something like that:
describe('TestComponent', () => {
let component: TestComponent;
let router: Router;
let fixture: ComponentFixture<TestComponent>;
const routerSpy = jasmine.createSpyObj('Router', ['navigate']); // create a router spy
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
declarations: [TestComponent],
providers: [
{ provide: Router, useValue: routerSpy } // use routerSpy against Router
],
}).compileComponents();
}));
beforeEach(() => {
router = TestBed.inject(Router); // get instance of router
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it(`should navigate to 'home' page`, () => {
component.navigateToHome(); // call router.navigate
const spy = router.navigate as jasmine.Spy; // create the navigate spy
const navArgs = spy.calls.first().args[0]; // get the spy values
expect(navArgs[0]).toBe('/home');
});
});
Inspired with angular docs: https://angular.io/guide/testing-components-scenarios#routing-component
I am new to unit testing angular/javascript apps. I needed a way to mock (or spy) for my unit test. The following line is borrowed from Experimenter and helped me TREMENDOUSLY!
const routerSpy = jasmine.createSpyObj('Router', ['navigate']); // create a router spy
I would like to say that I had no idea I could do that with Jasmine. Using that line above, allowed me to then create a spy on that object and verify it was called with the correct route value.
This is a great way to do unit testing without the need to have the testbed and all the ceremony around getting the testing module setup. Its also great because it still allows me to have a fake router object with out the need to stub in all of the parameters, methods, etc etc etc.

How to create test spec for angular 2 service with #Inject contructor

I am having a problem creating a unit test for my translate service. I have this as a constructor of my TranslateService
constructor(#Inject(TRANSLATIONS) private _translations: any) {}
I created my translate.service.spec.ts with this
describe('On initialize', () => {
it('No changes made to the service', async(inject([TranslateService], (service: TranslateService) => {
let translate = TestBed.get(TranslateService);
})));
});
However, the console prompts me with an error saying, "No provider for TranslateService". How inject the #Inject in the spec file?
If you using this
TestBed.configureTestingModule({
providers: [
{ provide: TRANSLATION, useClass: TranslationService }
]
});
Then you need to do this
it('should inject', inject([TRANSLATION], (value: TranslationService) => {
}));
The provide specifies the token for which we can inject. Since the token isn't TranslationService, we can't inject TranslationService. We need to inject by the token TRANSLATION
UPDATE
Is the inject is in the TranslationService
class TransactionService {
constructor(#Inject(TRANSLATION) value) {}
}
Then you should probably do something more like
TestBed.configureTestingModule({
providers: [
TranslationsService,
{ provide: TRANSLATION, useValue: whateverTranslationIs }
]
});
it('should inject', inject([TranslationService], (value: TranslationService) => {
}));
You need to configure the TRANSACTIONS injectable in the test bed configuration

Angular 2 - Mocking Services in Components that depend on other services

How do I mock a service that depends on another service that is in a component?
Please check code below.
a.component.ts
#Component({
selector: 'my-comp',
templateUrl: './my.component.html',
providers: [ MyServiceA ]
})
export class MyComponent {
my-service-a.service.ts
#Injectable()
export class MyServiceA{
constructor(private myServiceB: MyServiceB) {}
my-service-b.service.ts
export class MyServiceB{
constructor(private myServiceC: MyServiceC,
private myServiceD: MyServiceD) {}
How do I mock the service in the a.component.spec.ts in the TestBed configuration? Please help. Thank you.
You can mock it however you want. The other services don't matter. I think maybe the problem you are facing is with the #Component.providers. Using this, any mocks you configure in the TestBed aren't used as the #Component.providers take precedence, causing Angular to try to create it, instead of using the mock.
To get around that, Angular offers the TestBed.overrideComponent method, so that we can override things like the template and providers of the #Component
beforeEach(() => {
let myMockService = new MyMockService();
TestBed.configureTestingModule({
providers: [
// the following will not be used
{ provide: MyService, useValue: myMockService }
]
});
TestBed.overrideComponent(MyComponent, {
set: {
providers: [
// this will override the #Component.providers:[MyService]
{ provide: MyService, useValue: myMockService }
]
}
});
})

Angular 2 - When to use the inject function and when to not use it

I have a question in creating specs/unit tests in Angular2. Whenever you are injecting a mocked service, when do you use the inject function as the one below
it('function that the component calls',
inject([MyService], (service: MyService) => { // ...
}));
Or when do you use it as the one below
beforeEach(() => {
let myMockService = new MyMockService();
TestBed.configureTestingModule({
providers: [
{ provide: MyService, useValue: myMockService }
]
});
TestBed.overrideComponent(MyComponent, {
set: {
providers: [
{ provide: MyService, useValue: myMockService }
]
}
});
})
Can someone enlighten me on the matter?
The main reason to use it is to get access to the service in your test, when Angular is creating it. For instance
providers: [ MyService ]
Here Angular is creating it, and you only have access to it through Angular's injector.
But you're providing the service as a value then there is no need to use inject as you already have access to it
let serviceInstance = new Service();
provider: [ { provide: MyService, useValue: serviceInstance } ]
Here you already have access to serviceInstance so no need to get it from the injector.
Also if you don't need to access to the service inside the test, then there's not even a need to try and access it. But sometimes your mock will have thing you want to do with it inside the test.
Aside from inject, there are only ways to access the service
You could...
For your particular example you don't need inject at all. You just need to move the mock outside the scope of the beforeEach so that the it can use it
let myMockService = new MyMockService();
beforeEach(() => {
})
it('function that the component calls', () => {
myMockService.doSomething();
}));
You could...
Instead of using inject, you could get it from the TestBed, which acts like an injector. Maybe this is preferred as you can add it in your beforeEach
let service;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ MyService ]
});
service = TestBed.get(MyService);
})
it('function that the component calls', () => {
service.doSomething();
}));
You could...
Get it from the DebugElement which also acts like an injector
it('function that the component calls', () => {
let fixture = TestBed.createComponent(TestComponent);
let service = fixture.debugElement.get(MyService);
}));
So it's really a matter of preference. I personally try to stop using inject, as there are the other, less verbose options.
You use inject() when you want to get instances from the provide passed into your test code (for example the HTTP MockBackend) or any other service you want to communicate to directly in your test code.
TestBed.configureXxx only sets up providers for the test component.