The boilerplate reducing test utility for Angular. Supports Angular version 2.4.10 and greater, and running tests in in Chrome, Firefox, Edge, IE11, and Node (via JSDOM).
What is ng-unit?
ng-unit seeks to simplify unit testing of Angular components by providing automated mocking of child components, streamlined test setup, and easier DOM interaction to drastically the amount of boilerplate code needed.
An example
Suppose we want to mock out the child component used in the below component so we can assert that the component under test binds the correct value to its input.
Normally you would have to do something like this:
it"sets the child components input",
With ng-unit this simply becomes:
it"sets the child components input",
Installation
npm install --save-dev ng-unit
Setup
If you are using jasmine for mocking then no setup is needed. ng-unit will automatically use spys when it needs to mock methods. If you don't use jasmine for mocking, then you will need to register a provider for mocks before you begin your tests.
For example to use sinon stubs you would need to do the following before your tests
mockProvidersinon.stub
In an Angular CLI app you would put this in test.ts
ng-units documentation uses sinon stubs and chai assertions in all of it's examples
Guide
- Basic Testing
- Mocking child components
- Falling back to TestBed functionality
- Usage without test setup
Basic Testing
A simple test
ng-unit greatly simplifies setup and mocking for Angular TestBed tests. In the simplest scenario you simply need to
pass the component to be tested to testComponent()
and invoke .begin()
to instantiate your component. You can then
use element()
to query the DOM for elements.
it"has a greeting message",;
You can also select multiple elements with elements('.selector')
.
Simulating events
You can simulate DOM events by using trigger()
.
it"fires a click event handler",
Additionally you can optionally pass an object with properties to be added to the event object.
triggerelement'input', 'keydown',
Setting inputs element values
Value setter convenience methods for DOM inputs are provided. They automatically fire the appropriate change/input events on the input being set.
setTextInputValueelement"input[type=text]", "Sasquatch" //Text field now has value "Sasquatch"setTextAreaValueelement"textarea", "Sasquatch" //Text area now has value "Sasquatch"setCheckboxValueelement"input[type=check]", true //Checkbox is now checkedsetRadioButtonelement"input[type=radio]", true //Radio button is now selectedsetSelectValueelement"select", "Hancock" //Dropdown list now has the value "Hancock" selected
These work with any DOM element reference, not just those returned by ng-units selection methods. They can be used in traditional TestBed tests if desired.
Setting component inputs
Initial values for component inputs can be set prior to component instantiation (so they are properly present at
OnInit time) with the test builder method.setInput()
.
testComponentSubjectComponent .setInput"label", "presents" .begin
Once .begin()
is called you can change the input value with the setInput()
function.
testComponentSubjectComponent .setInput"label", "fizz" .begin setInput"label", "buzz"
Unlike directly setting input properties on the component under test directly, using setInput
will properly trigger
lifecycle methods such as ngOnChanges()
. Take note that, in order to change an input after after .begin()
is called
you must have given it an initial value while setting up the test.
Watching component outputs
Component outputs can be watched prior to component instantiation (so values emitted at OnInit time are not missed)
with .onOutput()
.
testComponentSubjectComponent .onOutput"save", persistevent .begin
Once .begin()
is called you can add new output watches with onOutput()
testComponentSubjectComponent .onOutput"save", persistevent .begin onOutput"save", console.logevent
Providing providers
Providers for services and other things can be registered with .providers()
testComponentSubjectComponent .providers .begin
Importing other modules
Other modules that your component under test depends upon can be imported using .import()
testComponentSubjectComponent .import .begin
Using schemas
Schemas can be registered with .schemas()
testComponentSubjectComponent .schemas .begin
Mocking child components
Child components can be mocked during test setup with .mock()
. When mocked a component will have a blank template
and require none of it's normal imports, providers, or child components to be registered for the test. This isolates
your tests from needing any knowledge of the children beyond what inputs you provide them, what outputs you subscribe
to, and any methods you call on the children directly.
it"renders transcluded content",
By default uses sinon for mocking functions. If you use Jasmine or another mocking library you can provide a factory
for your own mocks using mockProvider()
.
Interacting with mocked components
Child components can be selected with the component()
and components()
functions. You can query for children
using either CSS selector of the Component type.
Mock components have properties that correspond to their real versions to inputs, outputs, and methods.
You can assert that an input was set to a value by selecting the mock and asserting on the input property value.
expectcomponentChildComponent.greeting.to.equal"Hello World"
You can cause the mock child to emit and output by selecting the component and using the output event emitter that is created on the mock.
componentChildComponent.someOutput.emit"foo"
You can cause the mock child to emit and output by selecting the component and asserting on the mocked method.
expectcomponentChildComponent.someMethod.to.have.been.calledWith"bar"
Mocked methods can be setup before the component under test is instantiated, so you can set their initial return values.
testComponentSubjectComponent .mock .setupMockFooComponent, fooMock.getValue.returns"cake" .begin
Mocked components and transclusion
Mocked components automatically render and transcluded content so you can assert against it.
it"renders transcluded content",
Custom mock providers
By default ng-unit uses sinon stubs for mocking functions. You can configure your own mock provider if you prefer to use Jasmine spys or another mocking framework.
mockProviderjasmine.createSpy
Using real child components
If you want your test to utilize a real instances of child components configure them with .use()
. This can be useful
for doing integration tests that test numerous components. Take note that using a real child component also requires
you to register any imports, providers, and child components the component uses just like you were setting up a
traditional test bed test.
testComponentSubjectComponent .use .begin
Falling back to TestBed functionality
In the event that ng-unit does not allow you to test something in the desired way you can always fall back to TestBed
functionality by accessing the component fixture using fixture()
.
it"allows accessing the component fixture",
Usage without test setup
Even if you don't wish to use ng-units test setup, you can still take advantage of it's mocking, component selection, and assignment functionality.
Since setTextInputValue()
and the other input setting functions use DOM elements, it allows you to use elements
selected using test beds selection methods.
setTextInputValueinput, "foo"
Real or mocked child components can be selected even when not using testComponent()
by utilizing the selectComponent()
and selectComponents()
functions and providing the test fixture.
Mocking components can be accomplished by using the mockComponent()
function.
TestBed.configureTestingModule
Thanks to
SauceLabs for generously providing our platform for cross browser testing