50.003 - Software Testing
Learning Outcomes
By the end of this unit, you should be able to
- Explain what is software testing
- Articulate the purpose of software testing
- List the components of a test
- List the components of a test case
- Develop simple testsuite for JavaScript programs using Jest
Software testing
Software testing is one of the methods to check and validate the produced software is behaving according to the specification.
Furthermore software testing helps to identify potential flaws in
- architectural design
- software security
- software performance
The following is a quote adopted from IBM https://www.ibm.com/topics/software-testing
.
"Software testing is the process of evaluating and verifying that a software product or application does what it is supposed to do. The benefits of testing include preventing bugs, reducing development costs and improving performance."
Why software testing
The answer this question. One way is to find out what if we do not conduct software testing? In particular for large scale mission critical systems, the absence of testing often leads to epic failures and excessive monetary cost.
For instance in 1998, NASA launched a Mars Climate Orbiter. However due to an English Unit to Metric translation errors, the Orbiter lost contact after it passed behind Mars.
Anantomy of software testing
What is a test?
A test in a software system is an act to exercise software with test cases. A test have two goals
- to find faults.
- to show that the software behaves according to expectation (specification), i.e. to build confidence.
graph
Input-->Program
Program-->TO
TO["Test Oracle"]-->Pass
TO["Test Oracle"]-->Fail
A test is a process of feeding the generated input to the test subject, i.e. the program,
the output will be verified by a test oracle, which returns either Pass
or Fail
as result. The test oracle is often implemented as another program.
What is a test case?
A test case is a formal documentation of a test. A test case consists of
- an identifier (often linked to the user case identifier).
- a description of the purpose and the use case description being tested.
- a precondition
- a set of inputs
- the expected output
- the expected postcondition
- the execution history (the log)
Limitation of Software testing
Despite the importance of testing, testing alone is insufficient to ensure software free from bugs, since a failed test implies the existence of a software bug, a passed test implies that the software behaves according to the specification when given a particular instance of the input. But what about other input instances? What if the domain of the input is infinite.
"Testing shows the presence, not the absence of bugs" -- Edsger Dijkstra
Testing a Node.js app
There are many tools for testing Node.js app. Jest is the one of the most popular option.
To add Jest to a Node.js project, we can run
in the project description filepackage.json
change the follow line
to
Consider in the src/mymath.js
In the test/mymath.test.js
const mymath = require('../src/mymath.js');
describe("mymath sum test-suite", () => {
test ("summing of two positive numbers", () => {
const result = mymath.sum(1,2);
expect(result).toBe(3);
});
test ("summing of two negative numbers", () => {
const result = mymath.sum(-3,-2);
expect(result).toBe(-5);
});
})
The describe()
function defines a set of tests should be logically grouped together as a test suite. In the above case, in the test suite consists of two tests.
Each test is define by a call to test()
function, which expect a description fo the test, the action test function. In the test function, we compute the actual result by calling the test subject, sum()
, and compares it with the execpted result.
To execute the test, we run
which runs the above test suite against the test subject and generates the following report in the console.> my_test_app@1.0.0 test
> jest
PASS test/mymath.test.js
mymath sum tests
✓ summing of two positive numbers (12 ms)
✓ summing of two negative numbers (3 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 0.35 s, estimated 1 s
Ran all test suites.
Testing OOP
We can use Jest to test an object instance of a class, consider the following file
src/FibSeq.js
// src/FibSeq.js
class FibSeq {
constructor() {
this.prev = 0;
this.curr = 1;
}
next() {
let res = this.prev + this.curr;
this.prev = this.curr;
this.curr = res;
return res;
}
}
module.exports = FibSeq;
We can test it with the following test suite
const FibSeq = require('../src/FibSeq.js');
describe("FibSeq class test", () => {
const fibSeq = new FibSeq();
test ("first fib num is 1", () => {
const result = fibSeq.next();
expect(result).toBe(1);
});
test ("second fib num is 2", () => {
const result = fibSeq.next();
expect(result).toBe(2);
});
})
If we want to have the object being reset for each test case.
describe("FibSeq class test with setup and tear down", () =>{
let fibSeq = null;
beforeEach(() => {
fibSeq = new FibSeq();
});
test ("first fib num is 1 after reset", () => {
const result = fibSeq.next();
expect(result).toBe(1);
});
test ("second fib num is 1 after reset", () => {
const result = fibSeq.next();
expect(result).toBe(1);
});
afterEach(() => {
fibSeq = null;
})
})
Further Readings
- https://www.lambdatest.com/jest