4. Short answer:
Are you serious ?!
We just HATE deliver bad / malfunctioning code!
But we need to choose wisely what to test!
5. It’s like a war!
The operating scenario: «Angular Application».
•Composition of components and services (lots of moving parts).
•Interactions between components and services.
•Interactions between users and the application.
Best of all:
•Most of these Interactions are ASYNC!
6. We cannot face the fight alone!
We need some help:
•Take advantage of Unit Testing Frameworks, Tools & Utilities
•Surround the System Under Test with Test Doubles.
•Instrument the function calls and/or replace object behavior with something faked for testing
purposes.
•Deal with Asynchronous testing code.
7. Definition: Unit Testing
The definition is actually pretty vague!
We can define them by the signature points:
•Unit Tests focus on testing small parts (units) of the software system.
•Unit Tests are expected to verify few targetted assertions.
•Unit Tests are expected to be fast.
8. Definition: Test Double
Objects or Procedures that look and behave like their release-intended counterparts, but are
actually simplified versions that reduce the complexity and facilitate testing.
Like the Stunt-Doubles used in the film-making industry.
9. Several Types of Test Doubles
•Dummy objects are passed around but never actually used. Usually they are just used to fill
parameter lists.
•Fake objects actually have working implementations, but usually take some shortcut which makes
them not suitable for production (an InMemoryTestDatabase is a good example).
•Stubs provide canned answers to calls made during the test, usually not responding at all to anything
outside what's programmed in for the test.
•Spies are stubs that also record some information based on how they were called. One form of this
might be an email service that records how many messages it was sent.
•Mocks are pre-programmed with expectations which form a specification of the calls they are
expected to receive. They can throw an exception if they receive a call they don't expect and are
checked during verification to ensure they got all the calls they were expecting.
Source: martinfowler.com - https://martinfowler.com/bliki/TestDouble.html
12. Jasmine / Karma / Protractor
Jasmine is a behavior driven test framework, it can be used to test components, services and even
DOM elements.
http://jasmine.github.io/
Karma is a test runner that was designed to run low level Unit Tests (those that involve single
components, directives and services).
https://karma-runner.github.io
Protractor is an end to end test framework that runs tests against the real application running in a
real instance of the browser (without component mocking).
http://www.protractortest.org/
13. Preparing the battlefield
Setting up all these frameworks is outside the scope of this talk.
1. We can setup everything from scratch.
2. We can use seeding projects / startup projects with preconfigured environment.
3. Use the consolidated Angular CLI to generate the preconfigured project for us.
Let’s go with option 3, we can fine tune some configurations options later!
14. Karma / Jasmine Setup
The Angular CLI does the heavy lifting of setting up the whole testing environment for us:
ng test
With everything in place you can start using the Jasmine test framework together
with the Angular Testing Utilities to write your Unit Tests.
16. When thinking about testing you should:
•Work in a highly controlled environment.
•Keep the things simple, avoid using too many external libraries.
17. Two kinds of unit tests…
…that will allow us to learn how to write tests in an incremental way.
Pure Unit Testing (using Jasmine / Karma).
Angular Unit Testing (using Jasmine / Karma + Angular Testing Utilities).
19. Pure Unit Testing
Try to keep the things simple: just use what the frameworks offers us.
Good to test: Services, Pipes and Components'controllers.
Skip dependency injection, create instances of classes directly (using the constructors).
20. Pure Unit Testing
Pros:
•Easy to setup and understand.
•‘Very little’ to know, ‘just’ the test frameworks.
•Tests are easy to write.
Cons:
•Limited capabilities, you can only test services, pipes and components' controllers.
•Cannot test the DOM, nor the interaction between the component and its template (bindings).
•Testing a component might have little to no meaning at all (the component might not have any logic).
21. Pure Unit Tests
These kinds of test allows us to introduce:
•How Jasmine works (test setup & expectation checks).
•How to use some Test Doubles (dummy / fake / stubs / spies).
•How to do sync tests with Jasmine.
•How to do async tests with Jasmine.
•Learn techniques we can use in full Angular Tests.
22. TypeScript: an unexpected ally!
It helps us reducing the number of tests we need to setup:
•It helps us avoiding errors when we write tests, because it provide us:
• Intellisense.
• Code completion and code navigation.
• Syntax checking and type checking (even on some 'string literals', given the use of the new 'key of'
operator, see the spyOn sample).
•No more trivial tests to validate function parameters calls.
•No more trivial tests to check for implicit type conversion (or nullability, but depends on some
compiler switches).
25. Jasmine: test setup & sync tests
// Jasmine uses describe() to setup a test suite
describe('00 - Pure Test - Init - AuthService', () => {
let sut: AuthService;
// Jasmine runs the beforEach() functions before each test.
// Resets the configuration before each test, it will provide you
// with a clean environment every time.
// You can have more than one beforeEach() in a test suite, they will be executed in the correct order.
beforeEach(() => {
// no dependency injection, create everything with constructors
sut = new AuthService(new ApiAuthService());
});
// Jasmine uses 'it()' to define a single test case.
it("should login a user", () => {
sut.login("Alessandro", "12345");
expect(sut.isLoggedIn).toBeTruthy();
expect(sut.username).toBe("Alessandro");
});
app/unittests/00-pure-unit-tests/pure-tests-00-initialization.spec.ts
26. Jasmine: async tests & async/await
// Async Test: Jasmine will wait for a done() function call that will mark
// all the async test operation as completed.
it("(done()) should login a user", (done) => {
sut.loginAsync("Alessandro", "12345")
.then(result => {
expect(result).toBeTruthy();
expect(sut.isLoggedIn).toBeTruthy();
expect(sut.username).toBe("Alessandro");
// call done when all your expectation are verified!
done();
});
});
// Async Test, make it more readable with async/await;
// the function is still async, and we still might to call done().
it("(async/await) should login a user", async (done) => {
const result = await sut.loginAsync("Alessandro", "12345");
expect(result).toBeTruthy();
expect(sut.isLoggedIn).toBeTruthy();
expect(sut.username).toBe("Alessandro");
// call done when all your expectation are verified!
done();
});
app/unittests/00-pure-unit-tests/pure-tests-01-sync-async.spec.ts
Classic async test with promises
TypeScript async/await
27. Jasmine: async tests – done()
Forgetting to call the done() while doing async tests will lead to unpredictable results:
•Test displayed as green while no actual expectation has been already checked.
•Errors that will be displayed in the console only (if you have it open).
•Mixed results: the failing expectations can be displayed as part of the next test in line.
app/unittests/00-pure-unit-tests/pure-tests-01-sync-async.spec.ts
28. Jasmine: stub object
// What if our system under test has some dependencies ?
// Using the 'Real Services' is always a problem, we need to provide Test Doubles.
// Provide a Stub object (that implements the same interface of the original object)
// this way we can control the testing environment and test the component in isolation.
// It has the advantage of reducing the total number of dependencies of the unit.
describe('03 - Pure Test - using a Stub object', () => {
let authApiServiceStub: AuthApiService;
let sut: AuthService;
beforeEach(() => {
// create a stub object, we can control the test implementing / changing its behavior on the fly.
// we also have TypeScript to help us here (in case of refactoring).
authApiServiceStub = <AuthApiService>{};
authApiServiceStub.login = (username: string, password: string) => Promise.resolve(true);
sut = new AuthService(authApiServiceStub);
});
app/unittests/00-pure-unit-tests/pure-tests-03-stub.spec.ts
29. Jasmine: spy / spyOn
// What if our system under test has some dependencies ?
// Using the 'real services' is always a problem, we need to provide Test Doubles.
// Provide a Stub, this way we can control the testing environment and
// check the component in isolation.
// Use Jasmine 'spyOn' to instrument and change the behavior of the real object.
// We can also use it to check for expectations.
describe('02 - Pure Test - spyOn - AuthApiService', () => {
let authApiService: AuthApiService;
let sut: AuthService;
let loginSpy: jasmine.Spy;
beforeEach(() => {
authApiService = new AuthApiService();
sut = new AuthService(authApiService);
// a spy can be used to provide canned return values to function calls.
loginSpy = spyOn(authApiService, "login");
// .and.callThrough(); // instrument and delegate to the original function
// .and.returnValue(false); // provide a return value, can also provide custom behavior with 'callFake'
});
app/unittests/00-pure-unit-tests/pure-tests-02-spyOn.spec.ts
30. Jasmine: spy / spyOn
// Change behavior using a 'Spy'
it("should have no logged user if login fails", async (done) => {
// always fail the login procedure (this changes the service behavior)
loginSpy.and.returnValue(false);
const result = await sut.loginAsync("Alessandro", "12345");
expect(loginSpy.calls.count()).toBe(1);
expect(result).toBeFalsy();
expect(sut.isLoggedIn).toBeFalsy();
expect(sut.username).toBe("");
done();
});
it("should have no logged user if the http call fails (fake)", async (done) => {
// always fail the login procedure (this changes the service behavior)
loginSpy.and.callFake((username: string, password: string) => { throw new Error("http error!") });
try {
const result = await sut.loginAsync("Alessandro", "12345");
} catch (e) {
expect((e as Error).message).toBe("http error!");
}
expect(loginSpy.calls.count()).toBe(1);
expect(sut.isLoggedIn).toBeFalsy();
expect(sut.username).toBe("");
done();
});
app/unittests/00-pure-unit-tests/pure-tests-02-spyOn.spec.ts
31. Jasmine: stub & spy
// Jasmine can also mix both approaches:
// - create a stub object.
// - augment it using a spy to trace the function calls.
beforeEach(() => {
// create a stub object, we can control the test implementing its behavior on the
fly
authApiServiceStub = <AuthApiService>{};
authApiServiceStub.login = (username: string, password: string) =>
Promise.resolve(true);
loginSpy = spyOn(authApiServiceStub, "login");
sut = new AuthService(authApiServiceStub);
});
app/unittests/00-pure-unit-tests/pure-tests-04-stub-spyOn.spec.ts
32. Jasmine & Angular Utilities: async()
// async() will call jasmine done() function for us when all the async operation
// started in the async test zone complete themselves.
it("(async()) should login a user", async(() => {
sut.loginAsync("Alessandro", "12345")
.then(result => {
expect(result).toBeTruthy();
expect(sut.isLoggedIn).toBeTruthy();
expect(sut.username).toBe("Alessandro");
});
}));
app/unittests/00-pure-unit-tests/pure-tests-05-async-testing-utilities.spec.ts
done() will be called by Angular
33. Jasmine & Angular Utilities: fakeAsync()
// fakeAsync() + tick() allows us to write async tests in a more linear coding style:
// the tests appear to be synchronous.
// WARNING: cannot make XHR calls inside a fakeAsync zone!
it("(fakeAsync(), no spyOn) should login a user", fakeAsync(() => {
let result: boolean;
sut.loginAsync("Alessandro", "12345")
.then(res => result = res);
// tick() simulates the asynchronous passage of time.
tick(500); // the functions inside the async call have a 500 ms delay before completing
expect(result).toBeTruthy();
expect(sut.isLoggedIn).toBeTruthy();
expect(sut.username).toBe("Alessandro");
}));
There are still some problems with fakeAsync and async/await, setTimeout, setInterval, and some RxJS operators...
app/unittests/00-pure-unit-tests/pure-tests-05-async-testing-utilities.spec.ts
35. Angular Unit Testing
What if we want to simulate the component as if it's part of a 'real' Angular application ?
With the dependency injection, template rendering, bindings and DOM interactions?
Introducing:
TestBed and the Angular Testing Utilities!
36. Angular Unit Testing
Pros:
•Extremely powerful.
•Allows to test services and components (services, controllers, templates, bindings) in isolation.
•Allows to test components and services interactions (@Input, @Output).
Cons:
•Take into account async operations since the start.
•Need to consider the change detection mechanics (you must know how Angular works).
•Tests initialization can be complex, you need to configure a fake ‘NgModule' with lots of moving parts.
•Complex tests need a deep knowledge of Angular Testing Utilities and Test Doubles for components
(Http, Router, …).
37. TestBed - definition
A testbed (also "test bed") is a platform for conducting rigorous, transparent, and replicable
testing of scientific theories, computational tools, and new technologies.
In software development testbedding is a method of testing a particular module (function, class,
or library) in an isolated fashion.
It may be used as a proof of concept or when a new module is tested apart from the
program/system it will later be added to.
38. TestBed & Angular Testing Utilities
They are available importing: '@angular/core/testing'.
TestBed is used to create an instance of a dynamically generated NgModule that will be
configured to supply the system under test and all the needed Test Doubles (components,
directives, service mocks, etc...).
beforEach() test the whole module is resetted to the starting state.
39. TestBed - API
Use TestBed to configure a dynamically generated module environment:
•.configureTestingModule() - takes a metadata object very close to the one used to define a real
Angular module.
The default testing module is configured with basic declaratives and some Angular service substitutes that every tester needs.
•.compileComponents() - compile the testing module asynchronously. You must call this method
if any of the testing module components have a templateUrl or styleUrls property.
After calling compileComponents, the TestBed configuration is frozen for the duration of the current spec.
•.createComponent() - create an instance of a component of type T.
After calling compileComponent, the TestBed configuration is frozen for the duration of the current spec.
40. Angular Stand-alone Testing Utilities
Angular also provides some utility function that aim to simplify how tests are written.
Some of the most used:
•async() – runs the setup or the body of the test in a special async zone, ‘waiting’ for tasks to complete before
moving to the next step.
•fakeAsync() + tick() – runs the body of the test in a special fake async zone, allowing for a more linear control
flow.
•Inject() – injects services from the current TestBed instance.
•By – an helper class whose methods are used to build query predicates.
There are also many other functions that can be used to fine tune the tests (discardPeriodicTasks(), flushMicrotasks(), …).
43. Angular Tests - setup
describe('00 - Angular Tests - initialization - AppComponent', () => {
// Jarmine runs the beforEach() function before each test to
// reset the configuration of the testing TestBed NgModule before each test, it will provide you
// with a clean environment every time.
// You can have more than one beforeEach() in a test suite, they will be executed in the correct order.
beforeEach(async(() => {
// the component has external resources, and we need to give the compiler time to read them
// the async() Angular test function will arrange the things so that the code is run in a special async test zone.
// All the tests will be executed after any async operation started in the beforEach has been completed.
// TestBed is used to define a dynamically generated NgModule.
TestBed.configureTestingModule({
declarations: [
AppComponent
]
}).compileComponents();
// calling 'compileComponents()' locks down the testing module. No more configuration
// or override methods calls are allowed after this call.
// If you don't call .compileComponents(), the first call to .createComponent() will
// lock down the TestBed module too.
// WebPack users do not need to call .compileComponents(), everything is inlined during the build phase.
}));
app/unittests/01-angular-testing/angular-tests-00-initialization.spec.ts
44. ComponentFixture
TestBed.createComponent() returns a 'ComponentFixture', a construct that gives access to:
•componentInstance: a handle the component's controller.
•debugElement: a handle to the component's DOM element.
45. Angular Tests - ComponentFixture
it('should create the app', () => {
// use .createComponent() to get a ComponentFixture object.
// A ComponentFixture is what you use to gain access to the component (componentInstance)
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
expect(app.title).toBe("app");
});
it("should not have any title in the DOM until Angular calls for change detection.", () => {
// use .createComponent() to get a ComponentFixture object.
// A ComponentFixture is what you use to gain access to the component DOM nodes (debugElement)
const fixture = TestBed.createComponent(AppComponent);
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent.trim()).toEqual("Welcome to");
});
app/unittests/01-angular-testing/angular-tests-00-initialization.spec.ts
46. ChangeDetection
By Design(tm) Angular testing framework does not trigger Change Detection automatically, you need to do that
manually calling:
ComponentFixture.detectChanges()
•It gives us a chance to test the component in an uninitialized state, and check what happens to the template
before and after the bindings did their magic.
•It also means that: changing a property in the component as a synchronous operation will be invisible to
Angular untill we call detectChanges() explicitly (even with the automatic detect changes in place).
The default behavior can be changed setting:
{ provide: ComponentFixtureAutoDetect, useValue: true }
47. Angulat Tests – detectChanges()
// changeDetection() is not automatic, this is intentional, so the user
// has a chance to inspect the component before Angular does its magic.
// You can change this behavior configuring ComponentFixtureAutoDetect, like this:
// { provide: ComponentFixtureAutoDetect, useValue: true }
// The ComponentFixtureAutoDetect service responds to asynchronous activities such as promise resolution,
// timers, and DOM events.
it("should not have any title in the DOM until manually call 'detectChanged'", () => {
const fixture = TestBed.createComponent(AppComponent);
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent.trim()).toEqual("Welcome to");
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
// ask Angular to apply its change detection cycle (all the bindings will be evaluated).
// Angular responds to asynchronous activities such as promise resolution, timers, and DOM events.
// WARNING!!! A direct, synchronous update of the component property is invisible.
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
});
app/unittests/01-angular-testing/angular-tests-01-detectChanges.spec.ts
48. Component with Dependencies
In order to control the environment we need to provide substitutes for the component dependencies.
We can follow the same approaches we saw in the ‘Pure Tests’ scenarios:
•Provide a Stub Object.
•Spy on the Real Service.
•Spy on a Stub Object.
We’ll use dependency injection to supply services to our
components!
49. Component with Dependencies
There’s one recommendation:
if you need to interact with the injected services always get them from the component’s injector!
(accessible through the ComponentFixture.debugElement.injector property).
You can also get an instance of the service calling:
•TestBed.get()
•Inject()
50. Angular Tests – stub object / D.I.
// Always get the service from an Injector
// do not reference this instance inside the tests anymore, the instance that will be
// injected in the component is a clone - something different - of this object!
describe('02 - Angular Tests - with dep. - GreetingsComponent - Stub Injected Service',
() => {
let component: GreetingsComponent;
let fixture: ComponentFixture<GreetingsComponent>;
// the stub definition object
const authServiceStub = <AuthService>{
isLoggedIn: false
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [GreetingsComponent],
providers: [
{ provide: AuthService, useValue: authServiceStub }
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(GreetingsComponent);
component = fixture.componentInstance;
// avoid calling the detectChanges() automatically,
// we have a chance to interact with the services before the bindings are evaluated
// fixture.detectChanges();
});
// Get the service from an injector!
// 3 Ways to do it:
// 1- from the Component's Injector
// The way that will always work (angular has a ierarchy of injectors) is to ask the
component's injector
// (we can access it through the fixture).
it("should access the injected service from the Component's Injector", () => {
const auth = fixture.debugElement.injector.get(AuthService);
expect(auth).not.toBeNull();
});
// 2- from TestBed.get() if the service is registered with the module
it("should access the injected service from the TestBed.get()", () => {
const auth = TestBed.get(AuthService);
expect(auth).not.toBeNull();
});
// 3- using the inject() utility function if the service is registered with the module
it("should access the injected service from the TestBed.get()",
inject([AuthService], (auth) => {
expect(auth).not.toBeNull();
})
);
app/unittests/01-angular-testing/angular-tests-02-component-withDep.spec.ts
51. Angular Tests – async testing
Always take into account the async nature of Angular (starting from how change detection works):
•We can use the same techniques we saw in the ‘Pure Test’ scenarios.
•Angular offers us async() and fakeAsync() standalone helper functions to make our life way easier writing tests.
We already saw async() in action during the tests setup:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [GreetingsAsyncComponent],
providers: [AuthApiService, AuthService]
})
.compileComponents();
}));
app/unittests/01-angular-testing/angular-tests-03-component-async.spec.ts
52. … test setup context …
beforeEach(() => {
fixture = TestBed.createComponent(GreetingsAsyncComponent);
component = fixture.componentInstance;
authService = fixture.debugElement.injector.get(AuthService);
// add a spy that provides the test with the proper initialization
// we simulate having a user already authenticated, the server have to provide the
// data.
getLoggedUserAsyncSpy = spyOn(authService, "getLoggedUserAsync")
.and.returnValue(Promise.resolve("Alessandro"));
// .and.callThrough(); // delegating to functions with timers might create problems to Angular
testing utilities
el = fixture.debugElement.query(By.css("h3")).nativeElement;
// will call this in the actual tests, to check for initialized state
// fixture.detectChanges();
});
app/unittests/01-angular-testing/angular-tests-03-component-async.spec.ts
53. Angular Tests – async()
// async() - Angular Testing Utility function
it('should show greetings after getLoggedUserAsync promise (async)', async(() => {
// force change detection, calls ngOnInit
fixture.detectChanges();
// wait for the async function to complete (async test zone)
fixture.whenStable()
.then(() => {
// force a change detection to update the view
// (the tests does not do it automatically)
fixture.detectChanges();
expect(el.textContent).toBe("welcome, Alessandro");
});
}));
app/unittests/01-angular-testing/angular-tests-03-component-async.spec.ts
54. Angular Tests – fakeAsync() / tick()
// fakeAsync() - Angular Testing Utility function
// Enables a more linear coding style.
// WARNING: cannot make XHR calls inside a fakeAsync()
it('should show greetings after getLoggedUserAsync promise (fakeAsync)',
fakeAsync(() => {
// force change detection, calls ngOnInit
fixture.detectChanges();
// wait for the async functions to complete (fake async zone)
tick(); // use tick(500) when you set .and.callThrough() on the spy
// force a change detection to update the view
fixture.detectChanges();
expect(el.textContent).toBe("welcome, Alessandro");
}));
app/unittests/01-angular-testing/angular-tests-03-component-async.spec.ts
55. Angular Tests – async ‘the jasmine way’
// sometimes we need to use the traditional jasmine way to do async tests
// especially considering there may be problems with timers and xhr calls
it('should show greetings after getLoggedUserAsync promise (jasmine)', done => {
// force change detection, calls ngOnInit
fixture.detectChanges();
// use the spy to be notified when the inner function call ends
getLoggedUserAsyncSpy.calls.mostRecent().returnValue
.then(() => {
// force a change detection to update the view
fixture.detectChanges();
expect(el.textContent).toBe("welcome, Alessandro");
done();
});
});
app/unittests/01-angular-testing/angular-tests-03-component-async.spec.ts
56. Override Configuration
Sometimes registering the providers with the test module is not enough, a component can define
it's own dependency injection list, and we cannot directly stub the component's services in the
dynamically generated test module (they are declared in the component metadata).
What we can do is: override the component configuration and add, remove or set the metadata
of the component:
•TestBed.overrideComponent()
•TestBed.overrideDirective()
•TestBed.overrideModule()
•TestBed.overridePipe()
•TestBed.overrideTemplate()
.overrideComponent(MyComponent, {
set: {
providers: [
{ provide: Service, useClass: ServiceSpy}
]
}
})
.compileComponents();
57. entryComponents: []
TestBed actually does not have a way to define the entryComponents metadata.
Workaround:
•Define a new NgModule declaring all the needed entryComponents.
•Import the EntryComponents-NgModule in the TestBed.configureTestingModule() metadata.
58. Test @Input and @Output
Two strategies:
•Test the component stand-alone, emulating the interaction with the rest of the world.
Set the input properties and subscribe to the output properties
(we have access to component’s controller through the ComponentFixture).
•Create a Test Double (a very simple TestHostComponent), that can instantiate and use the
component under test. Use the test double to drive the nested component and verify what
happens (both in its controller and in its view).
60. Code Coverage
Code Coverage is a measure used to describe the degree to which the source code of a program
is executed when a particular test suite runs.
Angular CLI does the heavy lifting again and setup the code coverage analysis for us.
Run the tests with:
ng test --single-run=true --code-coverage
You can configure the report output in the karma.config.js file.
62. Integrate your build with VSTS
Configure Karma for:
•Single unit test execution.
•Output unit test result in a parsable format.
•Output code coverage information in a parsable format.
Configure VSTS (other services behave differently) to:
•On demand or Countinously build the application.
•Get the source files, ‘npm install’ the packages and build the application.
•Run the unit test suite.
•Display the build / tests / code coverage results.
63. Karma & Visual Studio Team Services
Karma.config.js
• Add a karma reporter plugin that can be parsed by VSTS: "karma-nunit2-reporter“.
• Configure karma to report the unit test results in “nunit” format.
• Configure Karma to output the code coverage analysis in “cobertura” format.
module.exports = function (config) {
config.set({
plugins: [
…
require('karma-nunit2-reporter')],
…
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly', 'cobertura' ],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml', 'nunit'],
nunitReporter: {
outputFile: 'test-results.xml',
suite: ''
},
…
});
};
package.json:
• Add the required plugin to emit the information in nunit format:
• create a script to execute the tests and the code coverage analisys.
• Test the script: “npm run vststest”
{
"name": "unit-tests",
"scripts": {
…
"vststest": "ng test --single-run=true --code-coverage --reporters nunit"
},
…
"devDependencies": {
…
"karma-nunit2-reporter": "0.3.0",
}
}