Property Testing¶
property testing is a sometimes useful approach to testing that says you should test that your function has properties of interest
consider the trim
function:
// returns a copy of s with all leading and trailing
// spaces removed
string trim(const string& s);
some properties of trim
include:
- if
s
consists of 0 or more spaces, thentrim(s) == ""
- for any string
s
,trim(trim(s)) == trim(s)
- for any string
s
,trim(reverse(s)) == reverse(trim(s))
(wherereverse(x)
is a function that returns the reverse of stringx
)
the idea of property testing is to write tests that give us confidence that these properties hold
it’s often possible to generate random test cases for such properties
for example, string(n, ' ')
returns a string of n
spaces, and so you
could write test code for the first property like this:
for(int i = 0; i < 100; i++) { // test code for property 1
int n = random_nonneg_int();
string all_spaces(n, ' ');
if (trim(all_spaces) == "") {
// test passed
} else {
// test failed on s
}
}
this tries 100 randomly-generated test inputs on trim
this style of testing encourages you to think about the high-level properties of your code
if you choose good properties, it can be quite effective
of course, like all testing, property testing can’t promise to catch all problems, and hand-crafted test cases are still useful for testing important inputs
on the plus side:
- it is usually easy to run many more tests (since they are being generated automatically)
- many programmers find it interesting and useful to come up with properties of their functions
Property Testing¶
here’s some code that can be used to randomly test some properties of
trim
:
const string possible_chars = "abcdefghijklmnopqrstuvwxyz0123456789~!@#$%^*()_+";
char rand_char() {
int r = rand() % possible_chars.size();
return possible_chars[r];
}
// returns random string of characters; 50% chance a character is a space
// (since spaces are important when testing trim)
string rand_string(int max_len = 10) {
int size = rand() % (max_len + 1);
string result(size, ' ');
for(int i = 0; i < size; i++) {
if (rand() % 2 == 0) {
result[i] = rand_char();
}
}
return result;
}
// returns a new string that is the character-by-character reverse of s
string reverse(string s) {
reverse(begin(s), end(s));
return s;
}
void trim_property_test() {
srand (time(NULL));
const int num_tests = 100;
for(int i = 0; i < num_tests; i++) {
string s = rand_string();
cout << "s = \"" << s << "\"\n";
assert(trim_bug1(trim_bug1(s)) == trim_bug1(s));
assert(trim_bug1(reverse(s)) == reverse(trim_bug1(s)));
}
cout << num_tests << " random tests passed\n";
}