1. 程式人生 > >Generative Testing in JavaScript

Generative Testing in JavaScript

Property-based Testing in Detail

Now that we have a high level idea about what property-based testing is all about it’s time to see how this works in a JavaScript setup.

The following examples are based on testcheck-js by Lee Byron, but there other alternatives like jsverify.

We have a handlePayment function that expects two arguments balance

and payment, balance representing the account total and payment representing a sum that will either be added or deducted from the total balance.

const handlePayment = (balance, payment) =>
(!payment || balance - payment <= 0) ? balance : balance - payment

In a traditional fashion you would start to write a couple of tests that verify certain expectations. For example your test might look something like this. You might test that handlePayment

handles zero as well as negative inputs besides checking for the standard case (positive balance and payment).

import { assert } from 'chai'
import
handlePayment from '../src/handlePayment'
describe('handlePayment', () => {
it('should handle zero inputs', () => {
assert.equal(handlePayment(0, 1), 0)
assert.equal(handlePayment(1, 0), 1)
})
it
('should handle negative inputs', () => {
assert.equal(handlePayment(-1, 1), -1)
assert.equal(handlePayment(10, -1), 11)
})
it('should handle positive inputs', () => {
assert.equal(handlePayment(200, 1), 199)
assert.equal(handlePayment(10, 11), 10)
})
})

We run the tests and see everything is green.

What property function would we need to define for verifying our assumptions?

Let’s verify that zero payment always returns balance.

import { check, property, gen } from 'testcheck';
import
handlePayment from './handlePayment'
const result = check(property([gen.int], (x) => {
return handlePayment(x, 0) === x
}))
console.log(result)

This is the log output:

{ result: true, ‘num-tests’: 100, seed: 1471133618254 }

testcheck-js ran one hundred tests against handlePayment and all cases passed. Now let’s verify if handlePayment works correctly when working with a balance of 100 and random payment inputs.

const result = check(property([gen.intWithin(0, 100)], x => {
return handlePayment(100, x) === 100 - x;
}))
console.log(result)

The result output shows clearly that a case has failed.

{ result: false,
'failing-size': 22,
'num-tests': 23,
fail: [ 100 ],
shrunk:
{ 'total-nodes-visited': 7,
depth: 0,
result: false,
smallest: [ 100 ] } }

If we take a closer look, we’ll find a shrunk property. This is a powerful feature that most generative testing libraries encompass. Instead of returning a large dataset of cases, where maybe one or a couple of cases failed, testcheck-js will try to find the smallest failing test (see the smallest property inside shrunk) as soon as one case fails. In this specific case the smallest value that failed is 100. This gives us very specific data to find out if the problem is inside the predicate function verifying our handlePayment or in handlePayment itself or if the dataset we generated isn’t explicit enough.

The dataset set should be fine. Let’s check the handlePayment function.

Obviously the case where balance and payment might be equal, in this failing case it’s [100, 100], isn’t handled properly. We verified that balance should return balance when payment is larger than the balance but didn’t cover this specific case. Let’s fix updatePayment.

const handlePayment = (balance, payment) =>
(!payment || balance - payment < 0 // fix. only less than zero
) ? balance : balance - payment

This will the solve the problem.

Let’s verify that paymentTransaction can handle a negative balance as well as a negative payment.

const { strictNegInt : strNegInt } = gen
const result = check(property([strNegInt, strNegInt], (x, y) => {
return handlePayment(x, y) === x - y;
}), {seed: 50})
console.log(result)

If you take a closer look at our new property, we added an options object, where we defined a seed, to be able to reproduce the generated tests. (Thanks Sune Simonsen for highlighting the fact)

Running the newly added test results in a new case failing.

{ result: false,
'failing-size': 1,
'num-tests': 2,
fail: [ -2, -1 ],
shrunk:
{ 'total-nodes-visited': 1,
depth: 0,
result: false,
smallest: [ -2, -1 ] } }

A balance of-2 and a payment of -1 would have a positive outcome on the balance. We need to refactor handlePayment again.

const handlePayment = (balance, payment) =>
(!payment ||
(balance <= 0 && balance - payment < balance) ||
(balance > 0 && balance - payment < 0)) ?
balance : balance - payment

Running the test again finally results in all cases passing.

{ result: true, 'num-tests': 100, seed: 50 }

Let’s refactor the handlePayment one last time, just to make it more readable.

const handlePayment = (balance, payment) =>
payment &&
((balance <= 0 && balance - payment > balance) ||
balance - payment >= 0) ?
balance - payment : balance

The tests still verify the property.

{ result: true, ‘num-tests’: 100, seed: 50 }

The two failed cases can be added to the previously defined unit tests.

We haven’t covered how to integrate testcheck-js with mocha or jasmine. This is out of scope for this writeup but there are specific mocha and jasmine testcheck-js wrappers available, see mocha-check and jasmine-check for more information on the topic. jsverify also has integration for mocha and jasmine.

相關推薦

Generative Testing in JavaScript

Property-based Testing in DetailNow that we have a high level idea about what property-based testing is all about it’s time to see how this works in a Java

[Unit Testing] Fundamentals of Testing in Javascript

sar help catch ret same develop more ESS cts In this lesson, we’ll get the most fundamental understanding of what an automated test

An Overview of JavaScript Testing in 2018

IntroLook at the logo of Jest, a testing framework by Facebook:As you can see, their slogan promises a “painless” JavaScript Testing, but as “some guy from

A Simple Example About Privileged Methods in JavaScript

following ont inner example sel html ans als Language Douglas Crockford classified the "class methods" in JavaScript into t

[Javascript] Compose multiple functions for new behavior in JavaScript

multi new cal you ucc bsp reduce start aries In this lesson you will create a utility function that allows you to quickly compose behavio

Deleting array elements in JavaScript - delete vs splice

ren make nor AC pos start print hang fine javascript 數組中刪除元素用 array.splice(start, deleteCount);這個方法。 ------------------------------------

單元測試優化的實踐(Generative Testing)

首先為什麼要寫單元測試? 因為對於任何一個軟體來說,“滿足需求”是他存在的必要條件,也是軟體的價值體現。單元測試一定是為它服務的。所以很容易知道寫單元測試的兩個動機:驅動(如:TDD)和驗證功能實現。另外,軟體需求“易變”的特徵決定了修改程式碼成為必然,在這種情況下,單元測試能保護已

單元測試優化的實踐Generative Testing

首先為什麼要寫單元測試? “滿足需求”是所有軟體存在的必要條件,單元測試一定是為它服務的。從這一點出發,我們可以總結出寫單元測試的兩個動機:驅動(如:TDD)和驗證功能實現。另外,軟體需求“易變”的特徵決定了修改程式碼成為必然,在這種情況下,單元測試能保護已有的功能不被破壞。 基於以上

理解javaScript中的作用域和上下文Understanding Scope and Context in JavaScript

譯者注:一直對於作用域和上下文感到很混亂,無意中看到這篇文章,覺得講得很好,故翻譯來與大家分享。翻譯不好之處,請大家多多指教。 原文連結:http://ryanmorr.com/understanding-scope-and-context-in-javascript/   前言部分,不做翻譯

[Algorithom] Stack Data Structure in JavaScript

A stack is a collection of items that obeys the principle of "last in, first out". Like a stack of plates, we can only access the topmost plate at any time

[Algorithoms] Linked List Data Structure in JavaScript

A linked list is a collection of items where each item points to the next one in the list. Because of this structure, linked lists are very slow when searc

[Algorithm] Write a Depth First Search Algorithm for Graphs in JavaScript

Depth first search is a graph search algorithm that starts at one node and uses recursion to travel as deeply down a path of neighboring nodes as po

[Algorithms] Refactor a Loop in JavaScript to Use Recursion

Recursion is when a function calls itself. This self calling function handles at least two cases, the recursive case and the base case. People seem

[Algorithms] Tree Data Structure in JavaScript

In a tree, nodes have a single parent node and may have many children nodes. They never have more than one parent nor point to any siblings. The

[Algorithms] Build a Binary Tree in JavaScript and Several Traversal Algorithms

A binary tree is a tree where each node may only have up to two children. These children are stored on the leftand right properties of each

[Algorithms] Sort an Array with a Nested for Loop using Insertion Sort in JavaScript

nsertion sort is another sorting algorithm that closely resembles how we might sort items in the physical world. We start at the second item in our collect

[Algorithms] Divide and Recurse Over an Array with Merge Sort in JavaScript

Merge sort is a recursive sorting algorithm. If you don't understand recursion, I recommend finding a resource to learn it. In brief, recursion is the act

object in javascript

列舉物件屬性 for....in 列舉obj的可列舉屬性,包括自身和原型鏈上的 object.keys() 只列舉物件本身的可列舉屬性 建立物件的幾種方式 物件字面量 const pre='test' const obj= { "name

《Log4j 2 官方文件》Testing in Maven

在 Maven 中使用測試 Maven在整個構建生命週期內可以執行單元測試和功能測試。預設情況下, 任何在 src/test/resources 路徑下的檔案都會複製到 target/test-classes 路徑中, 同時這些檔案在執行測試過程中,也會被包含在 classpath 中. 正因

[Algorithms] Solve Complex Problems in JavaScript with Dynamic Programming

Every dynamic programming algorithm starts with a grid. It entails solving subproblems and builds up to solving the big problem. Let’s break down a problem