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

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.

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.

How to fix undefined is not an object (evaluating 'newOptions.merge')?

I am writing angular 2 app. This test is created by angular cli and fails by default:
it('should render title in a h1 tag', async(() => {
let fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('app works!');
}));
Error message is:
Failed: Error in ./AppComponent class AppComponent - inline template:51:14 caused by: undefined is not an object (evaluating 'newOptions.merge')
mergeOptions#webpack:///~/#angular/http/src/http.js:47:0 <- src/test.ts:69434:22
get#webpack:///~/#angular/http/src/http.js:147:0 <- src/test.ts:69534:110
As I can found newOptions.merge is called from mergeOptions method inside node_modules/#angular/http/src/http.js.
What I have to do in order to make tests passing?
Here is gist fro app.component.ts
Her is gist for app.component.html
You are using Angular 2 Http service, so you need to inject defaultOptions parameter, which is, in your case, empty.
Best scenario to make this work is to add the following lines in your spec.ts file:
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{
provide: Http,
useFactory: (
backend: ConnectionBackend,
defaultOptions: BaseRequestOptions
) => {
return new Http(backend, defaultOptions);
},
deps: [BaseRequestOptions]
},
{
provide: BaseRequestOptions,
useClass: BaseRequestOptions
}
]
});
});
So, the line when you tell that provider BaseRequestOption uses class BaseRequestOptions is crucial.

How to write an HTTP mock unit test in Angular 2 final release?

I have moved from RC4 to final release (2.1.0) and I am refactoring my unit tests to conform to 2.1.0 syntax. This is easy except for HTTP mocking.
I cannot find any examples for how to mock HTTP requests in 2.1.0
Here is an RC4 HTTP unit test. How would I re-write this in final release 2.1.0?
it('ngOnInit()',
async(inject([TestComponentBuilder, XHRBackend], (tcb:TestComponentBuilder, mockBackend:MockBackend) => {
tcb.createAsync(Route1ListComponent).then((fix:ComponentFixture<Route1ListComponent>) => {
// THIS BLOCK OF CODE I NEED HELP TO RE-WRITE TO 2.1.0
mockBackend.connections.subscribe(
(connection:MockConnection) => {
connection.mockRespond(new Response(
new ResponseOptions({
body: persons
}
)));
});
// THIS BLOCK OF CODE WILL NOT CHANGE
let instance = fix.componentInstance;
instance.ngOnInit();
expect(instance.persons.length).toBe(3);
});
})));
NOTE: Do NOT provide RC code please. Thanks
First thing you need to do is configure the TestBed. There's no more TestComponentBuilder. With the TestBed, it's like configuring an #NgModule from scratch, just for the test environment. This means you will add the component under test to the declarations, add any providers to the provider, and any imports to the imports.
To configure the mock backend for Http provider, you would just create the Http from the MockBackend.
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ HttpModule ],
declarations: [ RouteListComponent ],
providers: [
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend: MockBackend, options: BaseRequestOptions) => {
return new Http(backend, options);
},
deps: [ MockBackend, BaseRequestOptions ]
}
]
})
})
That should be it for the configuration, assuming you don't need any other providers or imports I don't know about.
For the test you'll first want to make it an async test, as you'll be doing asynchronous operations in the test. This hasn't changed from RC, you just use async. If the component uses templateUrl (and you're not using Webpack), then you will need to call TestBed.compileComponents(), otherwise no need to. After that you can create the component with TestBed.createComponent
let fixture: ComponentFixture<RouteListComponent>;
let component: RouteListComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({ ... })
.compileComponents().then(() => {
fixture = TestBed.createComponent(RouteListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
}));
it('...', async(inject([MockBackend], (backend: MockBackend) => {
})))
Pretty much all the things above related to testing can be imported from #angular/core/testing. Your use of the MockBackend would still be the same.
Another note, you don't need to call the component.ngOnInit. That is called by the framework when you call fixture.detectChanges()
See Also:
Testing documentation for more complete examination of testing support.
Many thanks to #peeskillet for helping me reach my answer ..
import {APP_BASE_HREF} from '#angular/common';
import {async, ComponentFixture, TestBed} from '#angular/core/testing';
import {By} from '#angular/platform-browser';
import {AppModule} from '../../../app.module';
import {persons} from '../../../data/persons';
import {Route1ListComponent} from './route1-list.component';
// HTTP mocking imports
import {BaseRequestOptions, Http, Response, ResponseOptions} from '#angular/http';
import {MockBackend, MockConnection} from '#angular/http/testing';
describe('route1-list.component.ts', () => {
let fix: ComponentFixture<Route1ListComponent>;
let instance: Route1ListComponent;
let injector: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [AppModule],
providers: [{provide: APP_BASE_HREF, useValue: '/'},
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (pBackend: MockBackend, pOptions: BaseRequestOptions) => {
return new Http(pBackend, pOptions);
},
deps: [MockBackend, BaseRequestOptions]
}]
}).compileComponents()
.then(() => {
fix = TestBed.createComponent(Route1ListComponent);
instance = fix.componentInstance;
injector = fix.debugElement.injector;
});
}));
it('should instantiate component', () => {
expect(instance).toEqual(jasmine.any(Route1ListComponent));
});
it('should have expected text', () => {
let el = fix.debugElement.query(By.css('section.route1-list')).nativeElement;
expect(el.textContent).toMatch(/route 1 list view/i, 'should have expected text');
});
it('ngOnInit()', async(() => {
let backend = injector.get(MockBackend);
backend.connections.subscribe(
(connection: MockConnection) => {
connection.mockRespond(new Response(
new ResponseOptions({
body: persons
}
)));
});
fix.detectChanges(); // Calls instance.ngOnInit()
expect(instance.persons.length).toBe(3);
}));
it('ngOnInit() failure', async(() => {
let backend = injector.get(MockBackend);
backend.connections.subscribe(
(connection: MockConnection) => {
connection.mockError(new Error('error'));
});
fix.detectChanges(); // Calls instance.ngOnInit()
expect(instance.persons).toBeUndefined();
}));
});
Notice that, at the time of writing, the Angular2 docs at ..
https://angular.io/docs/ts/latest/api/http/testing/index/MockBackend-class.html
https://angular.io/docs/ts/latest/api/http/testing/index/MockConnection-class.html
seem to be wrong.
When I use Injector.resolveAndCreate as detailed in the docs I get the error:
Property 'resolveAndCreate' does not exist on type 'typeof Injector'.
To fix it I had to base my answer on the answer provided by #peeskillet

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