Jasmine
What and Why
What
Javascript testing framework
Can be used with different languages and frameworks
Javascript, Node
Typescript, Angular
Ruby
Python
Why
Fast
Independent
Runs via Node or in Browser
Behaviour Driven Development
- Attempts to describe tests in a human readable format
Setting up a test environment
For JS
<!-- Order of tags is important -->
<link rel="stylesheet" href="jasmine.css" />
<script src="jasmine.js"></script>
<script src="jasmine-html.js"></script>
<script src="boot.js"></script>
<!-- Sctipts to be tested -->
<script src="main.js"></script>
<!-- Test Script -->
<script src="test.js"></script>
For Node
Add Jasmine as a dev dependency
npm install --save-dev jasmine
Add test command to package.json
"scripts": { "test": "jasmine" }
Run the test command
npm test
Writing a test suite and spec
We want to test the following helloWorld
function.
main.js
function helloWorld() {
return "Hello world!"
}
This function, when called should return 'Hello World'
test.js
describe("suiteName", () => {
it("specName", () => {
expect(helloWorld()).matcher("Hello world!")
})
})
4 functions
- Define the Test Suite
describe(string, function)
- Define a Test Spec
it(string, function)
- Define the Actual Value
expect(actual)
- Define the Comparison
matcher(expected)
Setup and Teardown
let expected = "Hello World!"
- Initialize variable that are needed for testing
beforeAll(function)
- Called before all tests in a Suite are run
afterAll(function)
- Called after all tests in a Suite are run
beforeEach(function)
- Called before each test Spec is run
afterEach
- Called after each test Spec is run
Matchers
- The means of comparing the
actual
andexpected
values - Return a
boolean
indicating if a test passed or failed - Jasmine has some pre-built Matchers
expect(array).toContain(member)
expect(fn).toThrow(string)
expect(fn).toThrowError(string)
expect(instance).toBe(instance)
expect(mixed).toBeDefined()
expect(mixed).toBeFalsy()
expect(mixed).toBeNull()
expect(mixed).toBeTruthy()
expect(mixed).toBeUndefined()
expect(mixed).toEqual(mixed)
expect(mixed).toMatch(pattern)
expect(number).toBeCloseTo(number, decimalPlaces)
expect(number).toBeGreaterThan(number)
expect(number).toBeLessThan(number)
expect(number).toBeNaN()
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledTimes(number)
expect(spy).toHaveBeenCalledWith(...arguments)
Custom matchers can also be defined
Running tests
For JS we have 2 options
Open
index.html
in a browserRun
npm test
Jasmine Demo
The following 3 files
index.html
<!DOCTYPE html>
<html>
<head>
<title>Jasmine Running</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine.css"
/>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/boot.js"></script>
<script src="main.js"></script>
<script src="test.js"></script>
</body>
</html>
main.js
function helloWorld() {
return "Hello world!"
}
function byeWorld() {
return "Bye world!"
}
function bigNumber(num) {
return num + 1
}
test.js
describe("Test Suite", () => {
let hello = ""
let bye = "Bye World :("
let num = 0
beforeAll(() => {
num = 5
})
beforeEach(() => {
hello = "Hello world!"
})
afterEach(() => {
hello = ""
console.log("Test Completed")
})
it("Says hello", () => {
expect(helloWorld()).toEqual(hello)
})
it("Says bye", () => {
expect(byeWorld()).toEqual(bye)
})
it("Returns a bigger number", () => {
expect(bigNumber(num)).toBeGreaterThan(num)
})
})
Karma
What and Why
What
Test Runner for Jasmine in Angular
Automated running of tests
Does not require us to modify our code
Can test on multiple browser instances at once
Testing Angular Components
-
ng generate component <component>
will output:component.html
component.css
component.ts
component.spec.ts
Test against an instance of a component
Using the Angular Test Bed
Angular Test Bed
- Test behaviour that depends on Angular framework
- Test Change and Property Binding
- Import the
TestBed
,ComponentFixture
and Component to be tested
import { TestBed, async } from "@angular/core/testing"
import { AppComponent } from "./app.component"
- Configure the Test Bed's Testing Module with the necerssary components and imports in the beforeEach
- Reinstantiate the component before each test
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
...
});
Create a fixture and Component Instance
- wrapper for a Component and Template
const fixture = TestBed.createComponent(AppComponent) const app = fixture.debugElement.componentInstance
If a component has injected dependencies we can get these instances by:
const service = TestBed.get(ServiceName)
Looking at the App Component
app.component.html
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
</div>
<h2>Here are some links to help you start:</h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="...">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="...">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="...">Angular blog</a></h2>
</li>
</ul>
app.component.ts
import { Component } from "@angular/core"
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {
title = "app"
}
app.component.spec.ts
import { TestBed, async } from "@angular/core/testing"
import { AppComponent } from "./app.component"
describe("AppComponent", () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
}).compileComponents()
}))
it("should create the app", async(() => {
const fixture = TestBed.createComponent(AppComponent)
const app = fixture.debugElement.componentInstance
expect(app).toBeTruthy()
}))
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent)
const app = fixture.debugElement.componentInstance
expect(app.title).toEqual("app")
}))
it("should render title in a h1 tag", async(() => {
const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges()
const compiled = fixture.debugElement.nativeElement
expect(compiled.querySelector("h1").textContent).toContain(
"Welcome to app!"
)
}))
})
Running tests with the AngularCLI
ng test
- Other Angular features can also be tested
- Services
- Components
- Classes
- Forms
- Routing
- Dependency Injection
- etc.
Karma Demo
Conclusion
- Jasmine is a relatively simple testing tool
- Easy to implement on a variety of projects
- Karma Automates testing
- Test on multiple places simultaneously
- Well incorporated into Angular