- Angular is very well testable (Though there are a few surprises and the doc is bugged a bit).
- testing a pure TS class
- testing an Angular service
- testing an Angular service and mocking it's dependencies (via jasmine spies)
- testing an Angular component (via TestBed) and mocking it's dependencies
- shallow testing an Angular component (subcomponents are ignored; via NO_ERRORS_SCHEMA)
- e2e testing with protractor
- using asynch/await to achieve readable protractor tests
- using special HTML class (eg. .ee-*) to simplify selection by CSS
- default karma config is bugged (see Technical)
- mocking http is very hard (we must use Observable.defer(), NOT Observable.create())
- fixture.detectChanges() also calls .ngOnInit()
- jasmine doesn't need to be imported
- jasmine spy methods do NOT get reset before each test. You must create new spies if you want them fresh.
- ngModel cannot be tested via input.value (because it is simply empty). A special Angular attribute called "ng-reflect-model" can be used instead.
- "npm run e2e" takes ~1min to start and then ~2s per a trivial test case
- karma renders a preview of failing components to help debug the test. Very nice (we can eg. inspect the component via F12).
- to prevent uncontrollable rage (because chrome disconnects from karma every two seconds), use
// karma.conf.js
browserDisconnectTolerance: 10
- don't forget to exclude any test utility files from build compilation
// app/tsconfig.app.json
"exclude": [
"test.ts",
"test-utils",
"**/*.spec.ts"
]
Use it to let angular the angular test handle DI for you. However, you obviously need to mock out all the dependencies.
via NO_ERRORS_SCHEMA - see dashboard.component.spec.ts
- do I need GUI tests in components if I use e2e?
- Yes, they are much cheaper than e2e in terms of change-proofing. (also execution speed)
- why would I use shallow testing? isn't it enough if i just don't test subcomponents?
- Yes sure you can do that, but just to instantiate a component with many subcomponents you need to import all of them and mock ALL their dependencies. Good luck with that.
a better way to mock routes (possibly)
small isnpiration for e2e best practices
Run ng test
to execute the unit tests via Karma.
Unit tests use jasmine and TS and that code is run in google chrome browser via karma.
I strongly suggets using vscode > File > preferences > User snippets > TS
// press tab to use it!
"Arrow method": {
"prefix": ">>",
"body": [
"($1) => {$2}"
],
"description": "Empty arrow method"
},
"describe": {
"prefix": "d",
"body": [
"describe('$1', ()=>{",
"$2",
"});"
],
"description": "Jasmine describe() method"
},
"it": {
"prefix": "i",
"body": [
"it('$1', ()=>{",
"$2",
"});"
],
"description": "Jasmine it() method"
},
- version 2.6 jasmine-core (latest is 2.8, but angular packs 2.6)
- name must be *.spec.ts (karma and others expect that)
- describe() ~ test suite (~ tested class)
- it() ~ spec/test case (~ tested method, pre-conditions, post-conditions)
- expect() ~ assert
.toBe() - (===) LATER === or ==?, objects are the same (same internal identifier)
.toEqual() - objects have the same fields
.not.toBe()
.toBeNull()
.toBeUndefined()
.toMatch(/pattern/)
.toContain()
.toBeLessThan()
.toBeGreaterThan()
.toThrowError(TypeError)
- beforeEach(), afterEach(), beforeAll(), afterAll()
- fail()
- xit, xdescribe - a way to disable test suite/case
.toBeTruthy()
.toBeFalsy()
.toThrow()
these are too vague!
const instance = new Person();
spyOn(instance, 'getName') // calls spy instead of getName()
spyOn(obj, 'method').and.returnValue('Pow!') // return value by the spy
spyOn().and.callThrough(); // calls spy and THEN getName()
spyOn().and.callFake(() => { // a complex mock
if (x === 1) return 1;
else return 10;
});
spyOn().and.throwError(new Error); // spy throws error
var dummy = jasmine.createSpy('dummy') // a generic spy function, eg. for callbacks
$('button#mybutton').click(dummy)
jasmine.createSpyObj('HttpClient', ['get']); // a generic spy object with method .get()
expect(obj.method).toHaveBeenCalled()
expect(obj.method).toHaveBeenCalledWith('foo', 'bar')
obj.method.callCount
obj.method.mostRecentCall.args
obj.method.reset()
obj.method.argsForCall
-
karma spins up a web server, it connects to my browser (can be multiple, can be ghost browser, etc), karma sends the tests to browsers and they run there (probably websocket or sth)
-
how to test a class that uses a global library (eg. jquery):
- into karma.conf.js > files put sources files that you want loaded
- in spec.ts files use classic "declare var $:any" (funny thing: PHPStorm gives an error even thought this works when you run the tests!) source
Run ng e2e
to execute the end-to-end tests via Protractor.
Before running the tests make sure you are serving the app via ng serve
.
Super simple Angular app with 1 module and 2 routes. This is a mnor variation on the Tour of Heroes I wrote for the official docs.
git clone https://github.com/johnpapa/angular-tour-of-heroes.git toh
cd toh
npm i
Run ng serve
for a dev server. Navigate to http://localhost:4200/
. The app will automatically reload if you change any of the source files.
Run ng generate component component-name
to generate a new component. You can also use ng generate directive|pipe|service|class|module
.
Run ng build
to build the project. The build artifacts will be stored in the dist/
directory. Use the -prod
flag for a production build.
To get more help on the Angular CLI use ng help
or go check out the Angular CLI README.
This project was generated with Angular CLI version 1.2.0.