views:

56

answers:

1

Background:
I currently write test cases for the client side of a network protocol. As part of the tests I have to simulate several different expected and unexpected responses from the server (wrong header, connection lost, unexpected messages, timeouts).
Each of these test-cases can be accessed by its unique address.
My problem:
The current implementation is split into several parts:

  • An enum containing all the addresses of the test-cases
  • A class Tests containing all tests as static functions
  • A number of defines which map the function name to the corresponding enum + _FUNC
  • A wrapper class which takes an address a function and a name as argument
  • A method which returns a list of wrapper-class objects, one object for each test-case

For each message received the server checks the list of test-cases and either executes the test-case corresponding to the address or falls back to a default response.
The Question:
This implementation requires me to update at least five different locations for each new test-case and the class Test grows rather fast. Is there a good way to refactor it?
Example code:(this code is not valid c++)

enum TestAddress
{
   TEST_CONNECTION_BREAKDOWN = 0x100,
   TEST_ALL_IS_GOOD = 0x101,
}


class Wrapper : AbstrTestCase  //AbstrTestCase requires applies and test implementations
{ 
typedef testFun;
Wrapper(TestAddress addr,testFun func,string name)
boolean applies(int ad,...){return addr == ad;}
int test(...){return func(...);}
}
class Test
{
static int testConnectionBreakDownFunc (...)
static int testAllIsGoodFunc(...)
}
#define TEST_CONNECTION_BREAKDOWN_FUNC Test::testConnectionBreakDownFunc
#define TEST_ALL_IS_GOOD_FUNC Test::testAllIsGoodFunc

list<AbstrTestCase*> GetTests()
{
   list<AbstrTestCase*> tlist;
   tlist.push_back(new Wrapper(TEST_ALL_IS_GOOD,TEST_ALL_IS_GOOD_FUNC,"TEST_ALL_IS_GOOD"));
   ...
   tlist.push_back(new Wrapper(TEST_CONNECTION_BREAKDOWN,TEST_CONNECTION_BREAKDOWN_FUNC,"TEST_CONNECTION_BREAKDOWN_FUNC"));
   return tlist;
}

Related: My last atemp to at least clean up the code in GetTests() was an additional define for the arguments of wrapper

#define WRAP_ARGS(N) N,N##_FUNC,#N
tlist.push_back(new Wrapper(WRAP_ARGS(TEST_CONNECTION_BREAKDOWN));
#undef WRAP_ARGS(N)

while it results in cleaner looking code, it only hides the problem

A: 

I have had similar problems as you did. The approach I usually take is manage this via a script in order to automate the addition of code to the different places. A comment marks the point for the script to process the file so you don't need to write a complicated parser. Something like:

/* automatic code insertion point. Don't remove this comment */

Then you write a small script. There are many things macros can't do, and obscuring the code in order to write less is seldom a good idea. So write your script that updates the five locations.

piotr