Presentation is loading. Please wait.

Presentation is loading. Please wait.

Unit Testing.

Similar presentations


Presentation on theme: "Unit Testing."— Presentation transcript:

1 Unit Testing

2 Our First Program: Binary Search
Problem Statement: Write a function that consumes (1) a number to search for, and (2) an array of numbers sorted in ascending order. The function should produce true if the array contains the number and false if it does not. Examples: search(5, [2, 5]) // produces true search(4, [2, 5]) // produces false search(7, [2, 5, 7]) // produces true Type Signature: // search(v: number, a: number[]): boolean function search(v, a) { // TODO } function name argument name and type result type You should write the type of functions in a comment. It helps plan your code.

3 Step 1: Recursive pattern
// search(v: number, a: number[]): boolean function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { // FILL } else { }

4 Step 2: Fill in base-case
// search(v: number, a: number[]): boolean function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { return false; } else { // FILL } If the length of the array is 0, then there are no values in the array.

5 Step 4: Compare v to the middle element
// search(v: number, a: number[]): boolean function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { return false; } else { let mid = a.length / 2; if (a[mid] === v) { return true; } else if (a[mid] > v) { // FILL } else /* a[mid] < v */ { } Three cases: the value in the middle is equal

6 Step 5: Fill in recursive cases
// search(v: number, a: number[]): boolean function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { return false; } else { let mid = a.length / 2; if (a[mid] === v) { return true; } else if (a[mid] > v) { return search(v, a.slice(0, mid)); } else /* a[mid] < v */ { return search(v, a.slice(mid + 1, a.length)); } Note: a.slice method

7 Testing // search(v: number, a: number[]): boolean
function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { return false; } else { let mid = a.length / 2; if (a[mid] === v) { return true; } else if (a[mid] > v) { return search(v, a.slice(0, mid)); } else /* a[mid] < v */ { return search(v, a.slice(mid + 1, a.length)); } Do both these test cases work? search(5, [2, 5]) search(5, [2, 5, 8]) JavaScript does not have integer division: > 3 / 2 1.5

8 Bug fix // search(v: number, a: number[]): boolean
function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { return false; } else { let mid = Math.floor(a.length / 2); if (a[mid] === v) { return true; } else if (a[mid] > v) { return search(v, a.slice(0, mid)); } else /* a[mid] < v */ { return search(v, a.slice(mid + 1, a.length)); } These tests now work search(5, [2, 5]) search(5, [2, 5, 8]) Should we write more tests? Why not? More is better, right? Better question: do our tests cover all cases?

9 Test Coverage // search(v: number, a: number[]): boolean
function search(v, a) { // Since the length of the array is unknown, // we will recur on the length of a. if (a.length === 0) { return false; } else { let mid = Math.floor(a.length / 2); if (a[mid] === v) { return true; } else if (a[mid] > v) { } else /* a[mid] < v */ { } This implementation is obvious broken. But, these tests still work. search(5, [2, 5]) search(5, [2, 5, 8]) The problem is that the wrong lines of code are not executed. Think of length-3 arrays that: Execute the wrong lines and pass. These tests are not informative. Execute the wrong lines and fail. These tests are informative and reveal actual bugs. search(5, [1, 2, 3]); search(5, [1, 2, 5]);

10 (Some) Lessons About Testing
May need multiple test cases Question: How many? Tests should check execution in all branches of code This is called test coverage Tests should be informative Not executing the intended branches of code leads to incorrect results

11 Testing with Ocelot Name of the test Body of the test
test("Empty list", function() { let a = []; assert(search(1, a) === false); }); test("Mid match", function() { let a = [1, 4, 5]; assert(search(4, a) === true); test("Search upper half", function() { assert(search(5, a) === true); assert(search(4.5, a) === false); assert(search(7, a) === false); Name of the test Body of the test This is an anonymous function. We will study them in detail very soon. Assertions succeed only if provided a boolean expression that is true Can have multiple assertions

12

13 Mini-Program: Comparing Arrays
Problem Statement: Write a function that consumes two arrays of numbers. The function should produce true if the arrays have exactly the same values in-order, and otherwise false. Examples: arrayEqual([1, 2], [1, 2]) // produces true arrayEqual([1, 2], [2, 1]) // produces false arrayEqual([1, 2], [1, 2, 3]) // produces false Type Signature: // arrayEqual(a1: number[], a2: number[]): boolean function arrayEqual(a1, a2) { // TODO }

14 Mini-Program: Comparing Arrays
This will not work: // arrayEqual(a1: number[], a2: number[]): boolean function arrayEqual(a1, a2) { return a1 === a2; } Why? It compares references to the arrays, rather than the contents of the arrays. We will explore this in detail in lecture 7. Q: What properties should two arrays have, to declare that they are “equal”?

15 Mini-Program: Comparing Arrays
// arrayEqual(a1: number[], a2: number[]): boolean function arrayEqual(a1, a2) { if (a1.length !== a2.length) { return false; } for (let i = 0; i< a1.length; ++i) { if (a1[i] !== a2[i]) { return true; Q: What cases should we use to test this?

16 Mini-Program: Comparing Arrays
test("empty arrays", function() { assert(arrayEqual([], []) == true); }); test("unequal lengths", function() { assert(arrayEqual([1, 2], [1, 2, 3]) == false); assert(arrayEqual([2, 3], [1, 2, 3]) == false); test("equal lengths", function() { assert(arrayEqual([1, 2], [1, 2]) == true); assert(arrayEqual([1, 2], [2, 3]) == false);

17 Mini-Program: rootMagnitude
Problem Statement: Write a function that consumes an array of numbers and produces an array of numbers. Each element of the output array should be the square-root of the corresponding element in the output array, but with the same sign. Examples: Warning: Math.sqrt produces NaN when applied to a negative number! rootMagnitude([4, 9]) // produces [2, 3] rootMagnitude([0.16, 2.56]) // [0.4, 1.6] rootMagnitude([-16, -25, -100]) // [-4, -5, -10] rootMagnitude([4, -9]) // [2, -3] Type Signature: // rootMagnitude(a: number[]): number[] function rootMagnitude(a1, a2) { // TODO }

18 Attempt 1: update the array in-place
// rootMagnitude(a: number[]): number[] function rootMagnitude(a) { for (let i = 0; i < a.length; ++i) { if (a[i] >= 0) { a[i] = Math.sqrt(a[i]); } else { // TODO } return a; test("Test rootMagnitude", function() { assert(arrayEqual(rootMagnitude([4, 9]), [2, 3])); assert(arrayEqual(rootMagnitude([16, 100]), [4, 10])); }); We need a test case to execute this branch. Updating the array in Note: We use arrayEqual to compare the arrays.

19 Attempt 1: update the array in-place
// rootMagnitude(a: number[]): number[] function rootMagnitude(a) { for (let i = 0; i < a.length; ++i) { if (a[i] >= 0) { a[i] = Math.sqrt(a[i]); } else { a[i] = a[i]; } return a; test("negative elements", function() { assert(arrayEqual(rootMagnitude([4, -1]), [2, -1])); }); The test passes! But, the implementation in obviously wrong. Updating the array in The problem is we are updating the array in-place, so the input and output are identically. Rule of thumb: do not update arrays in-place

20 Attempt 2: produce a new array
// rootMagnitude(a: number[]): number[] function rootMagnitude(a) { let b = []; for (let i = 0; i < a.length; ++i) { if (a[i] >= 0) { b.push(Math.sqrt(a[i])); } else { b.push(-Math.sqrt(-a[i])); } return b; test("negative elements", function() { assert(arrayEqual(rootMagnitude([4, -1]), [2, -1])); }); Note: .push method of arrays adds an element to the end. Arrays in JavaScript are similar to ArrayLists in Java. Updating the array in

21 (Some) Lessons About Testing
May need multiple test cases Question: How many? Tests should check execution in all branches of code This is called test coverage Tests should be informative Not executing the intended branches of code leads to incorrect results Tests may need additional test infrastructure e.g. arrayEqual()


Download ppt "Unit Testing."

Similar presentations


Ads by Google