By: Victor Mejia
Does this describe your current front-end dev workflow?
https://angular.io/docs/ts/latest/guide/testing.html
describe('SuperAwesomeModule', function() {
describe('featureA', function() {
});
describe('featureB', function() {
});
});
it(<string>, <fn>)
describe('SuperAwesomeModule', function() {
describe('featureA', function() {
it('should calculate some super awesome calculation', function() {
...
});
it('should also do this correctly', function() {
...
});
});
});
expect(<actual>).<matcher(expectedValue)>
describe('SuperAwesomeModule', function() {
describe('featureA', function() {
it('should calculate some super awesome calculation', function() {
expect(SuperAwesomeModule.featureA([1, 2, 4]).toEqual(7);
});
it('should also do this correctly', function() {
expect(SuperAwesomeModule.featureB('...').toBe(true);
});
});
});
expect(foo).toBe(true); // uses JS strict equality
expect(foo).not.toBe(true);
expect(foo).toEqual(482); // uses deep equality, recursive search through objects
expect(foo).toBeDefined();
expect(foo).not.toBeDefined();
expect(foo).toBeUndefined();
expect(foo).toBeTruthy(); // boolean cast testing
expect(foo).toBeFalsy();
expect(foo).toContain('student'); // find item in array
expect(e).toBeLessThan(pi);
expect(pi).toBeGreaterThan(e);
expect(a).toBeCloseTo(b, 2); // a to be close to b by 2 decimal points
expect(function() {
foo(1, '2')
}).toThrowError();
expect(function() {
foo(1, '2')
}).toThrow(new Error('Invalid parameter type.')
describe("A spec using beforeEach and afterEach", function() {
var foo = 0;
beforeEach(function() {
foo += 1;
});
afterEach(function() {
foo = 0;
});
it("is just a function, so it can contain any code", function() {
expect(foo).toEqual(1);
});
it("can have more than one expectation", function() {
expect(foo).toEqual(1);
expect(true).toEqual(true);
});
});
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
describe('SuperAwesomeModule', function() {
xdescribe('featureA', function() {
it('should ...', function() {
});
it('should ...', function() {
});
});
describe('featureB', function() {
xit('should ...', function() {
});
it('should ...', function() {
});
});
});
describe('SuperAwesomeModule', function() {
beforeEach(function() {
// track all calls to SuperAwesomeModule.coolHelperFunction()
// and also delegate to the actual implementation
spyOn(SuperAwesomeModule, 'coolHelperFunction').and.callThrough();
});
describe('featureA', function() {
it('should ...', function() {
expect(SuperAwesomeModule.featureA(2)).toBe(5);
// matchers for spies
expect(SuperAwesomeModule.coolHelperFunction).toHaveBeenCalled();
expect(SuperAwesomeModule.coolHelperFunction).toHaveBeenCalledTimes(1);
});
});
});
describe('SuperAwesomeModule', function() {
beforeEach(function() {
spyOn(SuperAwesomeModule, 'coolHelperFunction').and.returnValue('myValue');
});
});
// list of files / patterns to load in the browser
files: [
'src/*.js',
'spec/*.js'
],
browsers: ['PhantomJS'], // run your tests in a headless browser!
update karma.conf.js:
plugins: [
require("karma-jasmine"),
require("karma-phantomjs-launcher"),
require("karma-spec-reporter")
],
...
reporters: ['spec'],
npm install husky --save-dev
// package.json
{
"scripts": {
"precommit": "npm test",
"prepush": "npm test",
"...": "..."
}
}
On npm install, that will install git commit hooks for you, and enable them by adding npm scripts
Just kidding :)
Sample App
import { TestBed } from '@angular/core/testing';
import { MyComponent } from './header.component';
describe('Component: MyComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [MyComponent]
});
TestBed.compileComponents();
});
});
Text
Set up each spec and configure the TestBed
let fixture = TestBed.createComponent(MyComponent);
https://angular.io/docs/ts/latest/api/core/testing/ComponentFixture-class.html
Returns a fixture for debugging and testing a component.
let elem = fixture.debugElement.nativeElement;
https://angular.io/docs/ts/latest/api/core/testing/ComponentFixture-class.html
Returns the debug element associated with this component
let component: HeaderComponent = fixture.debugElement.componentInstance;
https://angular.io/docs/ts/latest/api/core/testing/ComponentFixture-class.html
Returns the component instance.
it('should render a title', () => {
let fixture = TestBed.createComponent(HeaderComponent);
let elem = fixture.debugElement.nativeElement;
let component: HeaderComponent = fixture.debugElement.componentInstance;
expect(elem.querySelector('h1.header').innerHTML).toBe('ng2 storefront');
});
Services often require dependencies that Angular injects through the constructor of the service's class
@Injectable()
export class StorefrontService {
constructor(public http: Http) { }
getProducts(): Observable<Response> {
return this.http.get('/mock/products.json')
.map(res => res.json());
}
}
Setup: configure TestBed with providers
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (mockbackend: ConnectionBackend, defaultOptions: BaseRequestOptions) => {
return new Http(mockbackend, defaultOptions);
},
deps: [MockBackend, BaseRequestOptions]
},
StorefrontService,
]
});
});
For each test, use the "async" and "inject" functions in @angular/core/testing
use the mock connection to test requests
it('should call api with correct url',
async(inject(
[ApiService, MockBackend],
(apiService: ApiService, mockBackend: MockBackend) => {
mockBackend.connections.subscribe( (connection: MockConnection) => {
expect(connection.request.method).toBe(RequestMethod.Get);
expect(connection.request.url).toBe('/mock/products.json');
});
apiService.getProducts();
}
)
)
);
use the "providers" property when configuring the test bed
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
providers: [
{
provide: ApiService,
useClass: MockApiService
}
]
});
TestBed.compileComponents();
});
class MockApiService {
getProducts() {
return Promise.resolve(mockProductList);
}
}
Testing Pipes
@Pipe({
name: 'formatPrice'
})
export class FormatPricePipe implements PipeTransform {
transform(value: number): string {
return '$' + ( (value / 100).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",") );
}
}
Nothing special, it's just a class!
describe('Pipe: FormatPrice', () => {
let pipe: FormatPricePipe;
beforeEach(() => {
pipe = new FormatPricePipe();
});
it('should format price correclty', () => {
expect(pipe.transform(2499)).toBe('$24.99');
});
});