Better understanding of the requirements
More robust components
Avoid bugs created by a refactor
Feature docs for developers
Less buggy continuous delivery
Term “End to End testing” is defined as a testing method which determines whether the performance of application is as per the requirement or not. It is performed from start to finish under real world scenarios like communication of the application with hardware, network, database and other applications.
End-to-end testing is a technique used to test whether the flow of an application right from start to finish is behaving as expected. The purpose of performing end-to-end testing is to identify system dependencies and to ensure that the data integrity is maintained between various system components and systems.
Integration testing (sometimes called integration and testing, abbreviated I&T) is the phase in software testing in which individual software modules are combined and tested as a group.
Integration testing is a software testing methodology used to test individual software components or units of code to verify interaction between various software components and detect interface defects. Components are tested as a single group or organized in an iterative manner.
Software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.
Is a level of software testing where individual units/ components of a software are tested. The purpose is to validate that each unit of the software performs as designed.
Effort to write & run
Reliability
Test you tests: Is it failing when it needs to fail?
Does it work consistently?
Name your tests correctly: It will help you focus on what you need to test.
One logical assertion per test: It will keep the tests simple enough.
Keep them unit: It's too tempting to avoid mocks, but it will make debugging harder when they broke.
A good test fail report should give you information about:
My function should return the result of adding the stringified numbers in the array, as a number FAILED
Expected 1 to be 8.
1
2
3
4
A measure used to describe the degree to which the source code of a program is executed when a particular test suite runs.
myFunction(stringsArray: string[]) {
return stringsArray
.map(numString => parseInt(numString, 10))
.reduce((prev, current) => prev + current);
}
it('is a non sense test', () => {
const goodArray = ['1', '3', '2'];
expect(this.service.myFunction(goodArray)).toBeDefined();
});
it('is and incorrect test', () => {
const badArray = ['1', '3', 'L'];
expect(this.service.myFunction(badArray)).not.toBe(jasmine.any(String));
});
myFunction(stringsArray: string[]) {
return stringsArray
.map(numString => parseInt(numString, 10))
.filter(shouldBeNum => typeof shouldBeNum === 'number' ? shouldBeNum : 0)
.reduce((prev, current) => prev + current);
}
it('should add all the numbers in the array', () => {
const arrayToAdd = ['1', '3', '4'];
expect(this.service.myFunction(arrayToAdd)).toBe(8);
});
There's no magic %, it's up to your project's needs.
In case of doubt, test it.
In this case, just because. Most UT frameworks are really similar, choose the one that fits you and your app.
describe("Test suit name", function() {
var a;
it("should have at least one test", function() {
a = true;
expect(a).toBe(true);
a = undefined;
});
it("can have more than one", function() {
a = false;
expect(a).toBe(false);
a = undefined;
});
});
beforeEach and afterEach will run code before or after each test (it will run once per test).
beforeAll and afterAll will run code just once, before/after all the specs.
describe("Test suit name", function() {
var a;
afterEach(function() {
a = undefined;
})
it("will set a to undefined after this test", function() {
a = true;
expect(a).toBe(true);
});
it("will ALSO set a to undefined after this", function() {
a = false;
expect(a).toBe(false);
});
});
this allows to share variables between beforeEach, afterEach and it.
It's reseted for each test.
describe("Test suit name", function() {
beforeEach(function() {
this.a = 'invalid'
})
it("should have the initial value", function() {
expect(this.a).toBe('invalid');
this.a = true;
expect(this.a).toBe(true);
this.b = false;
});
it("is reset between tests", function() {
expect(this.b).toBe(undefined);
});
});
.not for negative assertions: expect(false).not.toBe(true)
.toEqual will compare the values of objects, but not their reference: expect(foo).toEqual(clonedFoo)
.toMatch for regular expresions: expect(message).toMatch(/bar/)
.toBeDefined, .toBeUndefined .toBeNull and .not.toBeNull are a fast way to pre-check if a variable or result is empty.
.toBeTruthy and .toBeFalsy for not boolean values:
expect(1).toBeTruthy(); expect(0).toBeFalsy()
.toThrow and .toThrowError for error management testing:
expect(foo).not.toThrow(); expect(foo).toThrow('F** you'); expect(foo).toThrowError('F** you')
A spy will keep track of the calls made to a function, and can modify the result.
It only exists in the context where it's declared
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = { setBar: function(value) { bar = value; }
};
spyOn(foo, 'setBar');
foo.setBar(123);
foo.setBar(456, 'another param');
});
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled();
});
it("tracks that the spy was called x times", function() {
expect(foo.setBar).toHaveBeenCalledTimes(2);
});
it("tracks all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
});
By default, a spy will prevent the execution of the function which is spying, but this behaviour can be modified with .and
.and.callThrough will execute the function
.and.returnValue will return what is declared, without executing the real function: spyOn(foo, 'getBar').and.returnValue('moked result');
.and.throwError: spyOn(foo, 'setBar').and.throwError('Test error');
.and.callFake will call a mock function:
spyOn(foo, "add").and.callFake(function(firstArg, secondArg) {
return firstArg + secondArg;
});
Angular is based in classes, which makes unit testing really easy
// import the component, service, pipe, etc to test
import { LikeComponent } from './like.component';
describe('LikeComponent', () => {
// declare a variable for the element to test
let component: LikeComponent;
beforeEach(() => {
// create a new instance of the element each time
// it's easier and safer the undo everything after every test
component = new LikeComponent();
});
it('should toggle the iLike property when I click it', () => {
component.iLike = true; // setup
component.click(); // trigger
expect(component.iLike).toBe(false); // verify
// clean up is done when component is reasigned
});
// write as many test as you need
});
TestBed creates an isolated testing module
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { VoterComponent } from './voter.component';
describe('VoterComponent', () => {
let component: VoterComponent;
let fixture: ComponentFixture<VoterComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
// Declare the component in the mocked module
declarations: [ VoterComponent ]
});
// the fixture is a reference to the environment around the created component
// it includes the DebugElement
fixture = TestBed.createComponent(VoterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should hightliht the button if I have upvoted', () => {
component.myVote = 1;
// fixture.detectChanges fires our changes in the module
// (updates dom, fires lifecycle hooks and events, etc)
fixture.detectChanges();
let de = fixture.debugElement.query(By.css('.glyphicon-menu-up'));
expect(de.classes['highlighted']).toBeTruthy();
});
});
Never use a real dependency in a UT
import { UsersComponent } from './users.component';
import { UserService } from './user.service';
import { Observable } from 'rxjs/Observable';
describe('UsersComponent', () => {
let component: UsersComponent;
let service: UserService;
beforeEach(() => {
service = new UserService(); // real instance of the dependency
component = new UsersComponent(service); // use it to initiate the component
});
it('should set users property with the users retrieved from the server', () => {
let users = [ 1, 2, 3 ];
// but SPY every call to it
spyOn(service, 'getUsers').and.returnValue(Observable.from([ users ]));
component.ngOnInit();
expect(component.users).toBe(users);
});
});
This method can be painfull if the service would have had a lot of dependencies itself. Here is another option:
https://angular.io/guide/testing#test-a-component-with-a-dependency