How to unit test the checkbox in Angular2 - unit-testing

I have a sample code for checkbox written with Angular2.
<div class="col-sm-7 align-left" *ngIf="Some-Condtion">
    <input type="checkbox" id="mob_Q1" value="Q1" />
    <label for="mob_Q1">Express</label>
</div>
I want to unit test the above checkbox. Like I want to recognize the checkbox and test whether it is check-able. How do I unit test this with Karma Jasmine?

Component, e.g. CheckboxComponent, contains input element. Unit test should looks like:
import {ComponentFixture, TestBed} from '#angular/core/testing';
import {By} from '#angular/platform-browser';
import {CheckboxComponent} from './checkbox.component';
describe('Checkbox test.', () => {
let comp: CheckboxComponent;
let fixture: ComponentFixture<CheckboxComponent>;
let input: Element;
beforeEach(() => {
TestBed.configureTestingModule(
{
declarations: [CheckboxComponent],
},
);
fixture = TestBed.createComponent(CheckboxComponent);
comp = fixture.componentInstance;
input = fixture.debugElement.query(By.css('#mob_Q1')).nativeElement;
});
it('should click change value', () => {
expect(input.checked).toBeFalsy(); // default state
input.click();
fixture.detectChanges();
expect(input.checked).toBeTruthy(); // state after click
});
});

IS there a need to write fixture.detectChanges()?
I went through the same test without this and it ends with success.
Button 1 is 'checked' by default
const button1 = debugElement.nativeElement.querySelector(selectorBtn1);
const button2 = debugElement.nativeElement.querySelector(selectorBtn2);
...
expect(button1.checked).toBeTruthy();
expect(button2.checked).toBeFalsy();
button2.click();
expect(button1.checked).toBeFalsy();
expect(button2.checked).toBeTruthy();
...

ngModel directive is async one and requires to use asynchronous capabilities of Angular unit testing. Adding async and whenStable functions.
it('checkbox is checked if value is true', async(() => {
component.model = true;
fixture.detectChanges();
fixture.whenStable().then(() => {
const inEl = fixture.debugElement.query(By.css('#mob_Q1'));
expect(inEl.nativeElement.checked).toBe(true);
});
}));
Source LinkLink

Related

debugElement return null in my angular5 test

I wish to test an anguar5 component using a host component as described in angular.io doc.
But my test keep failing because of
TypeError: Cannot read property 'query' of null
at UserContext.<anonymous> (http://localhost:9876/base/config-webpack/spec-bundle.js:293832:39)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/base/config-webpack/spec-bundle.js:288418:26)
at ProxyZoneSpec../node_modules/zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke (http://localhost:9876/base/config-webpack/spec-bundle.js:287920:39)
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/base/config-webpack/spec-bundle.js:288417:32)
at Zone../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9876/base/config-webpack/spec-bundle.js:288168:43)
at UserContext.<anonymous> (http://localhost:9876/base/config-webpack/spec-bundle.js:287799:34)
at attempt (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4289:46)
at ZoneQueueRunner.QueueRunner.run (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4217:20)
at ZoneQueueRunner.QueueRunner.execute (http://localhost:9876/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js:4199:10)
at ZoneQueueRunner../node_modules/zone.js/dist/jasmine-patch.js.jasmine.QueueRunner.ZoneQueueRunner.execute (http://localhost:9876/base/config-webpack/spec-bundle.js:287827:42)
Indeed, when I log my fixture.debugElement, it return null.
my test code is :
import {} from 'jasmine';
import { Component } from '#angular/core';
import { TestBed, ComponentFixture } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { DropDownListComponent } from './dropDownList.component';
#Component({
template: '<dropdown-list [valuesList]="valuesList" [selectedValue]="valuesSelected" ></dropdown-list>'
})
class TestComponent {
valuesList = [{label: 'test_select', value:'test'}, {label: 'test_select2', value:'test2'}];
valuesSelected = {label: 'test_select', value:'test'};
}
describe('DropDownListComponent', () => {
let fixture, component;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent, DropDownListComponent]
}).compileComponents();
});
it('should display selectedValue', () => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
console.log(fixture.isStable());
console.log(fixture);
console.log(component);
console.log(fixture.debugElement);
//const fixture = TestBed.createComponent(TestComponent);
let de = fixture.debugElement.query(By.css(".value-container"));
let el = de.nativeElement;
expect(el.textContent).toContain('test_select');
});
});
When you write a test you need to test only your component / service / pipe / directive etc, but not its dependencies.
From the code above I assume DropDownListComponent has a DI dependency that wasn't declared in providers of TestBed and it causes the issue. Anyway in this context it should be a mock, not a real component.
If you want to test DropDownListComponent - then please share its code. Without understanding its interface it's hard to guess how to write tests for it.
You can use ng-mocks to mock it and then you would only need to test that [valuesList] and [selectedValue] got right values.
Also to compile all components correctly you need to handle compileComponents's promise.
describe('TestComponent', () => {
beforeEach(() => {
return TestBed.configureTestingModule({
declarations: [TestComponent, MockComponent(DropDownListComponent)],
}).compileComponents();
});
it('should display selectedValue', () => {
const fixture = MockRender(TestComponent);
const dropdown = MockHelper.findOrFail(fixture.debugElement, DropDownListComponent);
expect(dropdown.valuesList).toEqual(fixture.point.valuesList);
expect(dropdown.selectedValue).toEqual(fixture.point.valuesSelected);
});
});
Profit.

How to test react component correctly?

Recently I am learning to test React with jest and enzyme, It seems hard to understand what a unit test is it, my code
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const value = e.target.value;
this.setState({
value
});
}
render() {
return <Nest value={this.state.value} handleChange={this.handleChange} />;
}
}
export const Nest = props => {
return <input value={props.value} onChange={props.handleChange} />;
};
export default App;
and my test
import React from "react";
import App, { Nest } from "./nest";
import { shallow, mount } from "enzyme";
it("should be goood", () => {
const handleChange = jest.fn();
const wrapper = mount(<App />);
wrapper.find("input").simulate("change", { target: { value: "test" } });
expect(handleChange).toHaveBeenCalledTimes(1);
});
IMO, the mocked handleClick will intercept the handleClick on App,
if this is totally wrong, what's the right way to use mock fn and test the handleClick be called.
Another: I search a lot, read the similar situations, seem like this iscontra-Unit Test,
Probably I should test the two component separately, I can test both components,
test the
<Nest value={value} handleChange={handleChange} />
by pass the props manually, and then handleChangeinvoked by simulate change
it passed test.
but how can I test the connection between the two?
I read
some work is React Team's Work
...
I don't know which parts I have to test in this case, and Which parts react already tested and don't need me to test. That's confusing.
You should take the path of testing the Nest component in isolation first, passing your mocked handleChange as a prop, to verify that input changes are being propagated.
If you want to test the state part, then you can get the instance of your App class from enzyme and call that method directly:
it("should update the Nest value prop when change is received", () => {
const wrapper = mount(<App />);
const instance = wrapper.instance()
instance.handleChange( { target: { value: "test" } })
const nestComponent = wrapper.find("Nest").first()
expect(nestComponent).prop('value').toEqual('test');
});
This a very very basic, almost not needed to test piece of code, but it will get your test coverage up if that's what you're after.
Doc for instance: http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html
If you want to test for the connection. From what I see, the nest component is a child component inside the App component. You could test that <App /> contains `.
describe('<App />', () => {
it('should contain a nest component', () => {
const wrapper = mount(<App />);
expect(wrapper.find(<Nest />)).toHaveLength(1);
});
});
Secondly, since the onChange event on the nest component updates the state in the App component, you can also test for state changes since its a behavior you expect.
it('should update state', () => {
//find input and simulate change with say {value: 'new value'} and then
expect(wrapper.state().value).toBe('newValue');
});
I hope this helps.

How to change value of a select box in angular2 unit test?

I have an Angular2 component that contains a select box that looks like
<select [(ngModel)]="envFilter" class="form-control" name="envSelector" (ngModelChange)="onChangeFilter($event)">
<option *ngFor="let env of envs" [ngValue]="env">{{env}}</option>
</select>
I am trying to write a unit test for the ngModelChange event. This is my latest failing attempt
it("should filter and show correct items", async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
el = fixture.debugElement.query(By.name("envSelector"));
fixture.detectChanges();
makeResponse([hist2, longhist]);
comp.envFilter = 'env3';
el.triggerEventHandler('change', {});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(comp.displayedHistory).toEqual(longhist);
});
});
The part I am having trouble with is that changing the value of the underlying model comp.envFilter = 'env3'; does not trigger the change method. I added el.triggerEventHandler('change', {}); but this throws Failed: Uncaught (in promise): ReferenceError: By is not defined. I cannot find any hints in the documentation... any ideas?
As far as the error. It seems like you just need to import By. This is not something that is global. It should be imported from the following module
import { By } from '#angular/platform-browser';
As far as the testing part, this is what I have been able to figure out. When you change a value in a the component, you need to trigger a change detection to update the view. You do this with fixture.detectChanges(). Once this is done, normally the view should be updated with the value.
From testing something similar to your example, it seems this is not the case though. It seems there is still some asynchronous task going on after the change detection. Say we have the following
const comp = fixture.componentInstance;
const select = fixture.debugElement.query(By.css('select'));
comp.selectedValue = 'a value';
fixture.DetectChanges();
expect(select.nativeElement.value).toEqual('1: a value');
This doesn't seem to work. It appears there is some async going on causing the value not to be set yet. So we need to wait for the async tasks by calling fixture.whenStable
comp.selectedValue = 'a value';
fixture.DetectChanges();
fixture.whenStable().then(() => {
expect(select.nativeElement.value).toEqual('1: a value');
});
The above would work. But now we need to trigger the change event as that doesn't happen automatically.
fixture.whenStable().then(() => {
expect(select.nativeElement.value).toEqual('1: a value');
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
fixture.whenStable().then(() => {
// component expectations here
});
});
Now we have another asynchronous task from the event. So we need to stabilize it again
Below is a complete test that I tested with. It's a refactor of the example from the source code integration tests. They used fakeAsync and tick which is similar to using async and whenStable. But with fakeAsync, you can't use templateUrl, so I though it would be best to refactor it to use async.
Also the source code tests does kind of a double one way testing, first testing model to view, then view to model. While it looks like your test was trying to do kind of a two-way test, from model around back to model. So I refactored it a bit to suite your example better.
import { Component } from '#angular/core';
import { TestBed, getTestBed, async } from '#angular/core/testing';
import { FormsModule } from '#angular/forms';
import { By } from '#angular/platform-browser';
import { dispatchEvent } from '#angular/platform-browser/testing/browser_util';
#Component({
selector: 'ng-model-select-form',
template: `
<select [(ngModel)]="selectedCity" (ngModelChange)="onSelected($event)">
<option *ngFor="let c of cities" [ngValue]="c"> {{c.name}} </option>
</select>
`
})
class NgModelSelectForm {
selectedCity: {[k: string]: string} = {};
cities: any[] = [];
onSelected(value) {
}
}
describe('component: NgModelSelectForm', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ FormsModule ],
declarations: [ NgModelSelectForm ]
});
});
it('should go from model to change event', async(() => {
const fixture = TestBed.createComponent(NgModelSelectForm);
const comp = fixture.componentInstance;
spyOn(comp, 'onSelected');
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
comp.selectedCity = comp.cities[1];
fixture.detectChanges();
const select = fixture.debugElement.query(By.css('select'));
fixture.whenStable().then(() => {
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(comp.onSelected).toHaveBeenCalledWith({name : 'NYC'});
console.log('after expect NYC');
});
});
}));
});
I found peeskillet's answer very useful but sadly it is a little out of date as the way to dispatch an Event has been changed. I also found there was an unnecessary call to whenStable(). So here is an updated test using peeskillet's setup:
it('should go from model to change event', async(() => {
const fixture = TestBed.createComponent(NgModelSelectForm);
const comp = fixture.componentInstance;
spyOn(comp, 'onSelected');
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
comp.selectedCity = comp.cities[1];
fixture.detectChanges();
const select = fixture.debugElement.query(By.css('select'));
fixture.whenStable().then(() => {
select.nativeElement.dispatchEvent(new Event('change'));
fixture.detectChanges();
expect(comp.onSelected).toHaveBeenCalledWith({name : 'NYC'});
console.log('after expect NYC');
});
}));
Look this example, from angular source (template_integration_spec.ts)
#Component({
selector: 'ng-model-select-form',
template: `
<select [(ngModel)]="selectedCity">
<option *ngFor="let c of cities" [ngValue]="c"> {{c.name}} </option>
</select>
`
})
class NgModelSelectForm {
selectedCity: {[k: string]: string} = {};
cities: any[] = [];
}
it('with option values that are objects', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelSelectForm);
const comp = fixture.componentInstance;
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
comp.selectedCity = comp.cities[1];
fixture.detectChanges();
tick();
const select = fixture.debugElement.query(By.css('select'));
const nycOption = fixture.debugElement.queryAll(By.css('option'))[1];
// model -> view
expect(select.nativeElement.value).toEqual('1: Object');
expect(nycOption.nativeElement.selected).toBe(true);
select.nativeElement.value = '2: Object';
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
tick();
// view -> model
expect(comp.selectedCity['name']).toEqual('Buffalo');
}));
Same problem as raised by OP but slightly different code.
Works in Angular 7.
HTML:
<select id="dashboard-filter" class="form-control" name="dashboard-filter" [ngModel]="dashboardFilterValue" (ngModelChange)="onFilterChange($event)"
[disabled]="disabled">
<option *ngFor="let filter of dashboardFilters" [ngValue]="filter.value">{{ filter.name }}</option>
</select>
Unit test:
it('onFilterChange', () => {
// ensure dropdown is enabled
expect(component.disabled).toBe(false)
// spies
spyOn(component, 'onFilterChange').and.callThrough()
spyOn(component.filterChange, 'emit')
// initially the 3rd item in the dropdown is selected
const INITIAL_FILTER_INDEX = 2
// we want to select the 5th item in the dropdown
const FILTER_INDEX = 4
// the expected filter value is the value of the 5th dashboard filter (as used to populate the dropdown)
const EXPECTED_FILTER_VALUE = getDashboardFiltersData.dashboardFilters[FILTER_INDEX].value
// handle on the dropdown
const filterDropdown = fixture.debugElement.query(By.css('select')).nativeElement
// let bindings complete
fixture.whenStable().then(() => {
// ensure filterDropdown.value is stable
expect(filterDropdown.value).toContain(getDashboardFiltersData.dashboardFilters[INITIAL_FILTER_INDEX].value)
// update filterDropdown.value and dispatch change event
filterDropdown.value = filterDropdown.options[FILTER_INDEX].value
filterDropdown.dispatchEvent(new Event('change'))
// check component data
expect(component.dashboardFilterValue).toBe(EXPECTED_FILTER_VALUE)
expect(component.dashboardFilterChangeInProgress).toBe(false)
// check spies
expect(component.onFilterChange).toHaveBeenCalledWith(EXPECTED_FILTER_VALUE)
expect(setDashboardFilterSpy).toHaveBeenCalledWith(EXPECTED_FILTER_VALUE)
expect(component.filterChange.emit).toHaveBeenCalledWith(true)
})
})

Updating input html field from within an Angular 2 test

I would like to change the value of an input field from within an Angular 2 unit test.
<input type="text" class="form-control" [(ngModel)]="abc.value" />
I can't just change the ngModel because abc object is private:
private abc: Abc = new Abc();
In Angular 2 testing, can I simulate the user typing into the input field so that the ngModel will be updated with what the user has typed from within a unit test?
I can grab the DebugElement and the nativeElement of the input field without a problem. (Just setting a the value property on the nativeElement of the input field doesn't seem to work as it doesn't update the ngModel with what I've set for the value).
Maybe inputDebugEl.triggerEventHandler can be called, but I'm not sure what arguments to give it so it will simulate the user having typed a particular string of input.
You're right that you can't just set the input, you also need to dispatch the 'input' event. Here is a function I wrote earlier this evening to input text:
function sendInput(text: string) {
inputElement.value = text;
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
return fixture.whenStable();
}
Here fixture is the ComponentFixture and inputElement is the relevant HTTPInputElement from the fixture's nativeElement. This returns a promise, so you'll probably have to resolve it sendInput('whatever').then(...).
In context: https://github.com/textbook/known-for-web/blob/52c8aec4c2699c2f146a33c07786e1e32891c8b6/src/app/actor/actor.component.spec.ts#L134
Update:
We had some issues getting this to work in Angular 2.1, it didn't like creating a new Event(...), so instead we did:
import { dispatchEvent } from '#angular/platform-browser/testing/browser-util';
...
function sendInput(text: string) {
inputElement.value = text;
dispatchEvent(fixture.nativeElement, 'input');
fixture.detectChanges();
return fixture.whenStable();
}
The accepted solution didn't quite work for me in Angular 2.4. The value I had set was not appearing in the (test) UI, even after detectChanges() was called.
The way I got it to work was to set up my test as follows:
describe('TemplateComponent', function () {
let comp: TemplateComponent;
let fixture: ComponentFixture<TemplateComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ FormsModule ],
declarations: [ TemplateComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TemplateComponent);
comp = fixture.componentInstance;
});
it('should allow us to set a bound input field', fakeAsync(() => {
setInputValue('#test2', 'Tommy');
expect(comp.personName).toEqual('Tommy');
}));
// must be called from within fakeAsync due to use of tick()
function setInputValue(selector: string, value: string) {
fixture.detectChanges();
tick();
let input = fixture.debugElement.query(By.css(selector)).nativeElement;
input.value = value;
input.dispatchEvent(new Event('input'));
tick();
}
});
My TemplateComponent component has a property named personName in this example, which was the model property I am binding to in my template:
<input id="test2" type="text" [(ngModel)]="personName" />
I also had trouble getting jonrsharpe's answer to work with Angular 2.4. I found that the calls to fixture.detectChanges() and fixture.whenStable() caused the form component to reset. It seems that some initialization function is still pending when the test starts. I solved this by adding extra calls to these methods before each test. Here is a snippet of my code:
beforeEach(() => {
TestBed.configureTestingModule({
// ...etc...
});
fixture = TestBed.createComponent(LoginComponent);
comp = fixture.componentInstance;
usernameBox = fixture.debugElement.query(By.css('input[name="username"]'));
passwordBox = fixture.debugElement.query(By.css('input[type="password"]'));
loginButton = fixture.debugElement.query(By.css('.btn-primary'));
formElement = fixture.debugElement.query(By.css('form'));
});
beforeEach(async(() => {
// The magic sauce!!
// Because this is in an async wrapper it will automatically wait
// for the call to whenStable() to complete
fixture.detectChanges();
fixture.whenStable();
}));
function sendInput(inputElement: any, text: string) {
inputElement.value = text;
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
return fixture.whenStable();
}
it('should log in correctly', async(() => {
sendInput(usernameBox.nativeElement, 'User1')
.then(() => {
return sendInput(passwordBox.nativeElement, 'Password1')
}).then(() => {
formElement.triggerEventHandler('submit', null);
fixture.detectChanges();
let spinner = fixture.debugElement.query(By.css('img'));
expect(Helper.isHidden(spinner)).toBeFalsy('Spinner should be visible');
// ...etc...
});
}));

angular2 test, how do I mock sub component

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.