How to test usage of ng-select in Angular applications
In order to test ng-select, we need to ensure that we are passing right
inputs / outputs into it.
Apart from that, we need to verify ng-templates if we are customizing ng-select.
Let's imagine that the template of a component uses ng-select in the next way:
<ng-select
[items]="cities"
groupBy="avatar"
[(ngModel)]="selectedCity"
bindLabel="name"
bindValue="name"
>
<!-- ng-label-tmp template -->
<ng-template ng-label-tmp let-item="item">
<strong>{{ item.name }}</strong>
</ng-template>
<!-- ng-optgroup-tmp template -->
<ng-template ng-optgroup-tmp let-item="item" let-index="index">
{{ index }}
<img [src]="item.avatar" [alt]="item.name" />
</ng-template>
<!-- ng-option-tmp template -->
<ng-template
ng-option-tmp
let-item="item"
let-index="index"
let-search="searchTerm"
>
<span class="ng-option-tmp">
{{ search }} {{ item.name }}
</span>
</ng-template>
</ng-select>
Therefore, to test it we need to:
- mock
ng-select - assert passed inputs
- assert listeners on outputs
- assert templates
Information about testing ng-template and its TemplateRef is taken from the ngMocks.render.
Spec file
The best way to mock everything except our component is to use MockBuilder.
Let's assume that the component is called TargetComponent and the module it belongs to is called TargetModule.
Then our beforeEach should look like that:
beforeEach(() => MockBuilder(TargetComponent, TargetModule));
Testing inputs of ng-select
There are 5 inputs we bind on ng-select based on the template of the component:
2 are bound to properties of the component's instance:
- items
- ngModel
3 are static:
- groupBy
- bindLabel
- bindValue
Therefore, to write a test, we need to use:
MockRender: to renderTargetComponentand get its instancengMocks.find: to find a debug element which belongs toNgSelectComponentngMocks.input: to get an input's value
it('binds inputs', () => {
// Rendering TargetComponent and accessing its instance.
const targetComponent =
MockRender(TargetComponent).point.componentInstance;
// Looking for a debug element of the ng-select.
const ngSelectEl = ngMocks.find('ng-select');
// Asserting bound properties.
expect(ngMocks.input(ngSelectEl, 'items')).toBe(
targetComponent.cities,
);
expect(ngMocks.input(ngSelectEl, 'ngModel')).toBe(
targetComponent.selectedCity,
);
// Asserting static properties.
expect(ngMocks.input(ngSelectEl, 'groupBy')).toEqual('avatar');
expect(ngMocks.input(ngSelectEl, 'bindLabel')).toEqual('name');
expect(ngMocks.input(ngSelectEl, 'bindValue')).toEqual('name');
});
Testing outputs of ng-select
There is 1 output we bind on ng-select.
Following Angular naming convention, the name of output for [(ngModel)] is ngModelChange.
Therefore, to convert it with a test, we need to use:
MockRender: to renderTargetComponentand get its instancengMocks.find: to find a debug element which belongs toNgSelectComponentngMocks.output: to get an output'sEventEmitter
it('binds outputs', () => {
// Rendering TargetComponent and accessing its instance.
const targetComponent =
MockRender(TargetComponent).point.componentInstance;
// Looking for a debug element of the ng-select.
const ngSelectEl = ngMocks.find('ng-select');
// Simulating an emit.
ngMocks.output(ngSelectEl, 'ngModelChange').emit('test');
// Asserting the effect of the emit.
expect(targetComponent.selectedCity).toEqual('test');
});
Testing ng-label-tmp template
To test a template of ng-select,
we need to find a debug element of ng-select,
then to find a template which belongs to ng-label-tmp,
and to render it with a proper context.
To write a test, we need to use:
MockRender: to renderTargetComponentand get its instancengMocks.find: to find a debug element which belongs toNgSelectComponentngMocks.findTemplateRef: to find the template which belongs tong-label-tmpngMocks.render: to render the template
it('provides correct template for ng-label-tmp', () => {
// Rendering TargetComponent.
MockRender(TargetComponent);
// Looking for a debug element of the ng-select.
const ngSelectEl = ngMocks.find('ng-select');
// Looking for the ng-label-tmp template
const ngLabelTmp = ngMocks.findTemplateRef(
ngSelectEl,
// attr name
['ng-label-tmp'],
);
// Verifies that ngSelect can access ngLabelTmp,
// and renders it.
ngMocks.render(
ngSelectEl.componentInstance,
ngLabelTmp,
{},
// Providing context variables.
{ item: { name: 'test' } },
);
// Asserting the rendered html.
expect(ngSelectEl.nativeElement.innerHTML).toContain(
'<strong>test</strong>',
);
});
Testing ng-optgroup-tmp template
The approach to test ng-optgroup-tmp is the same as above.
it('provides correct template for ng-optgroup-tmp', () => {
// Rendering TargetComponent and accessing its instance.
MockRender(TargetComponent);
// Looking for a debug element of the ng-select.
const ngSelectEl = ngMocks.find('ng-select');
// Looking for the ng-optgroup-tmp template
const ngOptgroupTmp = ngMocks.findTemplateRef(
ngSelectEl,
// attr name
['ng-optgroup-tmp'],
);
// Verifies that ngSelect can access ngOptgroupTmp,
// and renders it.
ngMocks.render(
ngSelectEl.componentInstance,
ngOptgroupTmp,
{},
// Providing context variables.
{
index: 7,
item: {
avatar: 'test.jpeg',
name: 'test',
},
},
);
// Asserting the rendered html.
expect(ngSelectEl.nativeElement.innerHTML).toContain(
'7 <img src="test.jpeg" alt="test">',
);
});
Testing ng-option-tmp template
The approach to test ng-option-tmp is the same as above.
it('provides correct template for ng-option-tmp', () => {
// Rendering TargetComponent and accessing its instance.
MockRender(TargetComponent);
// Looking for a debug element of the ng-select.
const ngSelectEl = ngMocks.find('ng-select');
// Looking for the ng-option-tmp template
const ngOptionTmp = ngMocks.findTemplateRef(
ngSelectEl,
// attr name
['ng-option-tmp'],
);
// Verifying that the instance has been mocked.
// And rendering its property,
// which points to the desired TemplateRef.
ngMocks.render(
ngSelectEl.componentInstance,
ngOptionTmp,
{},
// Providing context variables.
{
item: {
name: 'test',
},
searchTerm: 'search',
},
);
// Asserting the rendered html.
expect(ngSelectEl.nativeElement.innerHTML).toContain(
'<span class="ng-option-tmp">search test</span>',
);
});