I am using Angular 8 in my project but I have a problem with the unit-test when I have a component with ViewChild Ref in the unit-test is undefined. any help
I have one component
#Component({
selector: "app-rating-star",
templateUrl: "./rating-star.component.html",
styleUrls: ["./rating-star.component.scss"],
encapsulation: ViewEncapsulation.None
})
export class RatingStarComponent implements OnInit, AfterViewInit {
#ViewChild("measurementBoxStar") measurementBox: ElementRef;
constructor(private _render: Renderer2) {}
ngOnInit() {}
ngAfterViewInit() {
this._render.addClass(this.measurementBox.nativeElement, newClass);
}
}
and my unit-test for this component is
beforeEach(async(() => {
TestBed.configureTestingModule({
schemas: [NO_ERRORS_SCHEMA],
declarations: [RatingStarComponent],
providers: [
{
provide: Renderer2,
useClass: rootRendererMock
}
]
}).compileComponents();
fixture = TestBed.createComponent(RatingStarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it("check Input value for Box in red", () => {
component = fixture.componentInstance;
component.ngOnInit();
fixture.detectChanges();
component.ngAfterViewInit();
expect(component.valueStar).toEqual(1.702);
fixture.detectChanges();
expect(component.measurementBox.nativeElement.querySelector("span").innerText)
.toEqual("1.702");
});
when I run the unit-test, I received this error Error for Jasmine
obviously #ViewChild("measurementBoxStar") measurementBox: ElementRef; is not returning any elements. it may be because *ngIf="valueStar !== -1 && measurementName === ''" evaluates to false in tests. So changing your spec as follows should fix the problem.
it("check Input value for Box in red", () => {
component = fixture.componentInstance;
component.measurementName = "";
fixture.detectChanges();
expect(component.valueStar).toEqual(1.702);
expect(component.measurementBox.nativeElement.querySelector("span").innerText)
.toEqual("1.702");
});
Related
Hellow. I trying to test a component but i still doind it in the right way and i would like some help. This is the component:
export class ModalLoadingComponent implements OnInit {
#ViewChild('loading') loading: LoadingComponent;
constructor(public dialogRef: MatDialogRef<ModalLoadingComponent>, #Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit(): void {}
close() {
this.dialogRef.close();
}
and this is my test
fdescribe('CardsComponent', () => {
let component: ModalLoadingComponent;
let fixture: ComponentFixture<ModalLoadingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ModalLoadingComponent],
providers: [MatDialogRef],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ModalLoadingComponent);
component = fixture.componentInstance;
fixture.whenStable();
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I am trying to write a unit test of an angular 6 component which is initializing the bootstrap-daterangepicker in the ngAfterViewInit() method. When I run my unit test it gives the following error:
TypeError: $(...).daterangepicker is not a function
this is the code from the actual component(EmployeeComponent):
ngAfterViewInit(): void {
this.initializeDatePicker(this);
}
initializeDatePicker(that: any) {
const start = moment().subtract(7, 'days');
const end = moment();
$('#reportrange').daterangepicker({
startDate: start,
endDate: end,
maxDate: moment(),
ranges: {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')]
}
}, cb);
cb(start, end);
}
this is the code from my test class:
describe('EmployeeComponent', () => {
let component: EmployeeComponent;
let fixture: ComponentFixture<EmployeeComponent>;
let messageService: NotificationService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [EmployeeComponent]
})
.overrideComponent(EmployeeComponent, {
set: {
template: '',
providers: [
{ provide: NotificationService, useValue: messageService },
{ provide: ActivatedRoute, useValue: { queryParams: of({ emp: "123" }) } }
]
}
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EmployeeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
You don't need to handle it in your test cases. That component should be initialized in a separate service and you can simply mock that method from the service. In the way you can avoid this error.
let say you move all the code of the initializeDatePicker() in a method in some service let say common-service.ts and you can simply call that service from this method like
this.commonServiceObj.initializeDatePicker();
Now after doing this, you can simply mock initializeDatePicker() from the service object and error should be gone.
I'm attempting to test the two-way binding feature in Angular 2. I've also read through a few other answers but I still can't get the test to pass.
When the input field is updated, I would like to run a test that ensure the searchQuery property on the AppComponent class is the same as the value of the input field.
As mentioned, I've read a few other answers and as I've gone along included additional pieces of code. So what is there currently might not all be needed?
Component
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
template: '<input type="text" name="input" [(ngModel)]="searchQuery" (change)="onChange()" id="search">',
styles: ['']
})
export class AppComponent {
public searchQuery: string;
onChange() {
console.log(this.searchQuery);
}
}
Unit test
import { ComponentFixture, TestBed, async, fakeAsync, tick, ComponentFixtureAutoDetect } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { DebugElement } from '#angular/core';
import { AppComponent } from './app.component';
import { FormsModule } from '#angular/forms';
describe('AppComponent', () => {
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent ],
providers: [],
imports: [ FormsModule ],
schemas: []
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
comp = fixture.componentInstance;
});
it('should create the app', fakeAsync(() => {
const de = fixture.debugElement.query(By.css("#search"));
const el = de.nativeElement;
el.value = "My string";
var event = new Event('input', {
'bubbles': true,
'cancelable': true
});
el.dispatchEvent(event);
tick();
fixture.detectChanges();
expect(comp.searchQuery).toEqual("My string");
}));
});
If there is a better approach, I am of course happy to get any feedback around this.
You have to run
fixture.detectChanges();
before dispatching event to ensure that your control has initialized and registered onChange event
setUpControl function
// view -> model
dir.valueAccessor.registerOnChange(function (newValue) {
dir.viewToModelUpdate(newValue);
control.markAsDirty();
control.setValue(newValue, { emitModelToViewChange: false });
});
Plunker Example
See also
Angular2 NgModel not getting value in Jasmine test
I am trying to write some unit-tests on a component that got some services injected into it, to load the data from server. Data is loaded in this component on OnInit() method. I am trying that service method returns some dummy data, using spyOn. Following is unit-test setup -
let comp: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let staticDataService: any;
let spy: jasmine.Spy;
let allCountries: string[];
describe('MyComponent', () => {
beforeEach( async(() => {
TestBed.configureTestingModule({
imports : [ FormsModule, HttpModule ],
declarations : [MyComponent],
providers: [ StaticDataService ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
comp = fixture.componentInstance;
staticDataService = fixture.debugElement.injector.get(StaticDataService);
allCountries = [] = ["US", "UK"];
spy = spyOn(staticDataService, 'getCountries').and.returnValue(Promise.resolve(allCountries));
});
it('Countries should be set', () => {
expect(comp.allCountries).toEqual(allCountries);
});
});
Following is the component class that I am unit-testing -
#Component({
moduleId: module.id,
selector: 'myeditor',
templateUrl: 'my.component.html',
styleUrls: ['my.component.css']
})
export class MyComponent implements OnInit {
allCountries: string[];
constructor(private _staticDataServices: StaticDataService) {}
ngOnInit() {
this.getDataFromServer();
}
getDataFromServer()
{
this.allCountries = this._staticDataServices.getCountries();
}
I am getting the following error -
Chrome 53.0.2785 (Windows 7 0.0.0) MyComponent Countries should be set FAILED
[1] Expected undefined to equal [ 'US', 'UK' ].
Under the same unit-tests few other tests are working fine, that are not dependent on injected services. Getting 'undefined' while testing the properties that are set by services.
Can someone please help what I am doing wrong here?
Thanks
You need to call fixture.detectChanges() for the ngOnInit to be called.
fixture = TestBed.createComponent(MyComponent);
fixture.detectChanges();
getCountries returns a Promise so you need to then it, otherwise the value of allCountries will just be promise and not the data
getDataFromServer() {
this._staticDataServices.getCountries().then(data => {
this.countries = data;
});
}
Since the promise is asynchronous, you need to use async and wait for the asynchronous task to complete by calling fixture.whenStable()
import { async } from '#angular/core/testing';
it('...', async(() => {
fixture.whenStable().then(() => {
expect(comp.allCountries).toEqual(allCountries);
})
})
UDPATE
Without seeing the StaticDataService, I'm guessing you are trying to inject Http into it. This wont work in a test environment without further configuration. What I suggest you do is just make the service a mock
staticDataService = {
getCountries: jasmine.createSpy('getCountries').and.returnValue(...);
}
providers: [
{ provide: StaticDataService, useValue: staticDataService }
]
How do I mock sub component in jasmine tests?
I have MyComponent, which uses MyNavbarComponent and MyToolbarComponent
import {Component} from 'angular2/core';
import {MyNavbarComponent} from './my-navbar.component';
import {MyToolbarComponent} from './my-toolbar.component';
#Component({
selector: 'my-app',
template: `
<my-toolbar></my-toolbar>
{{foo}}
<my-navbar></my-navbar>
`,
directives: [MyNavbarComponent, MyToolbarComponent]
})
export class MyComponent {}
When I test this component, I do not want to load and test those two sub components; MyNavbarComponent, MyToolbarComponent, so I want to mock it.
I know how to mock with services using provide(MyService, useClass(...)), but I have no idea how to mock directives; components;
beforeEach(() => {
setBaseTestProviders(
TEST_BROWSER_PLATFORM_PROVIDERS,
TEST_BROWSER_APPLICATION_PROVIDERS
);
//TODO: want to mock unnecessary directives for this component test
// which are MyNavbarComponent and MyToolbarComponent
})
it('should bind to {{foo}}', injectAsync([TestComponentBuilder], (tcb) => {
return tcb.createAsync(MyComponent).then((fixture) => {
let DOM = fixture.nativeElement;
let myComponent = fixture.componentInstance;
myComponent.foo = 'FOO';
fixture.detectChanges();
expect(DOM.innerHTML).toMatch('FOO');
});
});
Here is my plunker example;
http://plnkr.co/edit/q1l1y8?p=preview
As requested, I'm posting another answer about how to mock sub components with input/output:
So Lets start by saying we have TaskListComponent that displays tasks, and refreshes whenever one of them is clicked:
<div id="task-list">
<div *ngFor="let task of (tasks$ | async)">
<app-task [task]="task" (click)="refresh()"></app-task>
</div>
</div>
app-task is a sub component with the [task] input and the (click) output.
Ok great, now we want to write tests for my TaskListComponent and of course we don't want to test the real app-taskcomponent.
so as #Klas suggested we can configure our TestModule with:
schemas: [CUSTOM_ELEMENTS_SCHEMA]
We might not get any errors at either build or runtime, but we won't be able to test much other than the existence of the sub component.
So how can we mock sub components?
First we'll define a mock directive for our sub component (same selector):
#Directive({
selector: 'app-task'
})
class MockTaskDirective {
#Input('task')
public task: ITask;
#Output('click')
public clickEmitter = new EventEmitter<void>();
}
Now we'll declare it in the testing module:
let fixture : ComponentFixture<TaskListComponent>;
let cmp : TaskListComponent;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TaskListComponent, **MockTaskDirective**],
// schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{
provide: TasksService,
useClass: MockService
}
]
});
fixture = TestBed.createComponent(TaskListComponent);
**fixture.autoDetectChanges();**
cmp = fixture.componentInstance;
});
Notice that because the generation of sub component of the fixture is happening asynchronously after its creation, we activate its autoDetectChanges feature.
In our tests, we can now query for the directive, access its DebugElement's injector, and get our mock directive instance through it:
import { By } from '#angular/platform-browser';
const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective));
const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective;
[This part should usually be in the beforeEach section, for cleaner code.]
From here, the tests are a piece of cake :)
it('should contain task component', ()=> {
// Arrange.
const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective));
// Assert.
expect(mockTaskEl).toBeTruthy();
});
it('should pass down task object', ()=>{
// Arrange.
const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective));
const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective;
// Assert.
expect(mockTaskCmp.task).toBeTruthy();
expect(mockTaskCmp.task.name).toBe('1');
});
it('should refresh when task is clicked', ()=> {
// Arrange
spyOn(cmp, 'refresh');
const mockTaskEl = fixture.debugElement.query(By.directive(MockTaskDirective));
const mockTaskCmp = mockTaskEl.injector.get(MockTaskDirective) as MockTaskDirective;
// Act.
mockTaskCmp.clickEmitter.emit();
// Assert.
expect(cmp.refresh).toHaveBeenCalled();
});
If you use schemas: [CUSTOM_ELEMENTS_SCHEMA]in TestBed the component under test will not load sub components.
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { TestBed, async } from '#angular/core/testing';
import { MyComponent } from './my.component';
describe('App', () => {
beforeEach(() => {
TestBed
.configureTestingModule({
declarations: [
MyComponent
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});
it(`should have as title 'app works!'`, async(() => {
let fixture = TestBed.createComponent(MyComponent);
let app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('Todo List');
}));
});
This works in the released version of Angular 2.0.
Full code sample here.
An alternative to CUSTOM_ELEMENTS_SCHEMA is NO_ERRORS_SCHEMA
Thanks to Eric Martinez, I found this solution.
We can use overrideDirective function which is documented here,
https://angular.io/docs/ts/latest/api/testing/TestComponentBuilder-class.html
It takes three prarmeters;
1. Component to implement
2. Child component to override
3. Mock component
Resolved solution is here at http://plnkr.co/edit/a71wxC?p=preview
This is the code example from the plunker
import {MyNavbarComponent} from '../src/my-navbar.component';
import {MyToolbarComponent} from '../src/my-toolbar.component';
#Component({template:''})
class EmptyComponent{}
describe('MyComponent', () => {
beforeEach(injectAsync([TestComponentBuilder], (tcb) => {
return tcb
.overrideDirective(MyComponent, MyNavbarComponent, EmptyComponent)
.overrideDirective(MyComponent, MyToolbarComponent, EmptyComponent)
.createAsync(MyComponent)
.then((componentFixture: ComponentFixture) => {
this.fixture = componentFixture;
});
));
it('should bind to {{foo}}', () => {
let el = this.fixture.nativeElement;
let myComponent = this.fixture.componentInstance;
myComponent.foo = 'FOO';
fixture.detectChanges();
expect(el.innerHTML).toMatch('FOO');
});
});
I put together a simple MockComponent module to help make this a little easier:
import { TestBed } from '#angular/core/testing';
import { MyComponent } from './src/my.component';
import { MockComponent } from 'ng2-mock-component';
describe('MyComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
MyComponent,
MockComponent({
selector: 'my-subcomponent',
inputs: ['someInput'],
outputs: [ 'someOutput' ]
})
]
});
let fixture = TestBed.createComponent(MyComponent);
...
});
...
});
It's available at
https://www.npmjs.com/package/ng2-mock-component.