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

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 }
]
}
});
})

Related

redis mock for testing in nestjs

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.

Unit testing Angular 2 components inside a common library NPM Package no app context

I am kind of stumped on this. I have used the Angular 2 quick start projects as a reference for unit testing Angular 2 but it seems to assume you have an app in play. In my case we have NPM packages that have Angular 2 modules in them that are shared across various projects in our organization. I would like to be able to unit test the code inside these common libraries in isolation (without them being part of an app).
I am looking for examples or a tutorial or something explaining the best approach to this, Google has not provided any help.
Well I am doing in my Karma test something like:
Create a mock component
#Component({
template: "",
selector: 'mock'
})
export class MockComponent implements OnInit {
constructor() { }
ngOnInit() {
console.log("Is loaduing");
}
}
Create a mock service
class MockSomeService {
public subscribe(){}
public inizialize() {}
}
Create ROUTES array
export var ROUTES = [ {path:"/pathexample", component: MockComponent}]
Create DECLARATIONS array
export var DECLARATIONS:Component[] = [
MockComponent, ExampleComponent
];
Create PROVIDERS
const CONSTANTS_PROVIDERS: Provider[] = [
{ provide: SomeService, useClass: MockSomeService }
];
Write a test
describe('Component: example', () => {
beforeEach(() => {
TestBed.configureTestingModule({ declarations: DECLARATIONS, providers: CONSTANTS_PROVIDERS, imports: [RouterTestingModule.withRoutes(ROUTES)] });
});
it('should create an instance', inject([ExampleComponent], (component: ExampleComponent) => {
expect(component).toBeTruthy();
}));
});
If your component is using route.navigate you should use TestBed.overrideComponent and add template: '<router-outlet></router-outlet>' to your component if not have it yet and actually create the component like this TestBed.createComponent(ExampleComponent);

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 - 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.

angular 2 rc7 - testing service - Error: No provider for String

I've getting an error in angular 2 testing using the webpack quickstart project.
Error: No provider for String! in config/karma-test-shim.js
I've never seen this error where the provider for String is missing. I figured out its related the private url:string in the services constructor but how do I resolve it?
Here's my testfile
describe('http.ts',()=>{
beforeEach(()=>{
TestBed.configureTestingModule({
providers:[
SbHttp,
MockBackend,
BaseRequestOptions,
{ provide: Http, useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}, deps: [MockBackend, BaseRequestOptions]}
],
imports: [
HttpModule
]
})
});
afterEach(()=>{
TestBed.resetTestingModule()
});
it('get() should work', inject([SbHttp],(sbHttp:SbHttp)=>{
expect(true).toBeTruthy()
}))
})
and here's the SbHttp service
#Injectable()
export class SbHttp{
private baseUrl:string;
constructor( private url:string, private headers:Headers, private http:Http
){
this.baseUrl = utils.stripTrailingSlash(url)
}
}
If I change to private url:any I'm getting this error
Can't resolve all parameters for SbHttp: (?, Headers, Http).
You need to make it injectable by creating a token, using #Inject() to inject with the token, and then add it as a provider in the providers list .
import { OpaqueToken, Injct } from '#angular/core';
const APP_URL = new OpaqueToken('app.url');
class SbHttp {
constructor(#Inject(APP_URL) url: string, ...) {}
}
TestBed.configureTestingModule({
providers: [
{ provide: APP_URL, useValue: 'http://localhost' }
]
});
You'll also need to configure the providers in the real app also. As for the Headers, I'm not sure, but I think you might get the error also once you fix this one. I'm not sure where you expect to be getting that from.
I highly suggest you take a look at the dependency injection chapter from the Angular documentation if you are new to DI with Angular.