The behavior of a function or method needs to be alterable.
Some languages allow functions to be treated as data. In this case we can declare functions to be fields instead of methods. Of course fields can be updated.
Assume objects are to represent function tests. By making the function to be tested a member variable rather than a member function we can reuse the same test class.
The run method compares the expected output-- as determined by the oracle-- with the actual output-- as determined by the function pointer. If one discrepancy is found, the test fails:
class Test
{
private:
int (*fun)(int); // pluggable
adapter
map<int, int> oracle;
public:
Test(int (*fun)(int)) { this->fun =
fun; }
void setFun(int (*fun)(int)) {
this->fun = fun; }
void add(int input, int output) {
oracle[input] = output; }
bool run()
{
bool pass = true;
map<int, int>::iterator p;
for(p = oracle.begin(); pass
&& (p != oracle.end()); p++)
pass = pass &&
(p->second == fun(p->first));
return pass;
}
};
Assume two functions are declared:
int fact(int n)
{
int result = 1;
for(int i = 1; i <= n; i++) result
*= i;
return result;
}
int tri(int n)
{
int result = 0;
for(int i = 1; i <= n; i++) result
+= i;
return result;
}
This function tests both functions:
void testAllFuns()
{
Test factTest(fact);
factTest.add(3, 6);
factTest.add(4, 24);
factTest.add(5, 120);
Test triTest(tri);
triTest.add(3, 6);
triTest.add(4, 10);
triTest.add(5, 15);
cout << "pass = "
<< (factTest.run() && triTest.run()) << endl;
}
This code can be improved by using the composite pattern to distinguish between composite tests (called test suites) and primitive tests (called test cases).
Another improvement is to make the TestCase class a template parameterized by the domain and range of the pluggable adapter.
Objects representing functions are called functors:
class Functor
{
private:
int (*fun)(int);
public:
Functor(int (*fun)(int) = 0) {
this->fun = fun; }
int operator()(int x)
{
if (!fun) throw
exception("function not defined");
return fun(x);
}
};
Here is a modified version of Test that uses functors:
class Test
{
private:
Functor fun;
map<int, int> oracle;
public:
Test(Functor fun) { this->fun = fun;
}
void add(int input, int output) {
oracle[input] = output; }
bool run()
{
bool pass = true;
map<int, int>::iterator p;
for(p = oracle.begin(); pass
&& (p != oracle.end()); p++)
pass = pass && (p->second
== fun(p->first));
return pass;
}
};