Jump to content

  • Log In with Google      Sign In   
  • Create Account


Unit test -- test different modules with same interface


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
7 replies to this topic

#1 wqking   Members   -  Reputation: 756

Like
-1Likes
Like

Posted 29 November 2011 - 09:41 PM

In brief, how to unit test an interface, which are implemented by different modules, efficiently?

For example,
I have an interface (an interface or a class, what ever, it doesn't matter), Animal.

class Animal
{
public:
  virtual bool canEat();
  virtual bool eat();
  // blah blah
};

I have several modules implementing that interface, such as Cat, Dog, etc. Assume Cat and Dog only expose the interface Animal, we don't need anything else from them.

OK, now I need to unit test Cat and Dog. Of course I can write tests for them separately, but that's not efficient and will produce almost same and duplicated code. Because they share the same public interface and logic.

So my question is, how can I test on Animal, but for Cat and Dog, separately, with the same code?

I can write

void testAnimal(Animal *);

MYTEST()
{
  testAnimal(new Cat);
  testAnimal(new Dog);
}

Then I can reuse the test code, but then if any test fails, it will be hard to trace which is wrong, Cat or Dog, because one test is doing two things.

Any suggestions and experience?


Thanks

http://www.cpgf.org/
cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.
v1.5.5 was released. Now supports tween and timeline for ease animation.


Sponsor:

#2 Adam_42   Crossbones+   -  Reputation: 2422

Like
0Likes
Like

Posted 30 November 2011 - 08:39 AM

Here's one suggestion:

void testAnimal(Animal *, const char *name);

#define TEST_ANIMAL(class) testAnimal(new class, #class)

MYTEST()
{
  TEST_ANIMAL(Cat);
  TEST_ANIMAL(Dog);
}

See http://msdn.microsoft.com/en-US/library/7e3a913x%28v=VS.80%29.aspx if you've not come across the # operator before.

#3 Telastyn   Crossbones+   -  Reputation: 3726

Like
0Likes
Like

Posted 30 November 2011 - 08:50 AM

What prevents this?

void testAnimal(Animal *);

TESTCAT()
{
  testAnimal(new Cat);
}

TESTDOG()
{
  testAnimal(new Dog);
}

You're unit testing the implementations, then do that. They might share a test implementation if they abide by the same contracts.

#4 wqking   Members   -  Reputation: 756

Like
-1Likes
Like

Posted 30 November 2011 - 08:59 AM

What prevents this?

void testAnimal(Animal *);

TESTCAT()
{
  testAnimal(new Cat);
}

TESTDOG()
{
  testAnimal(new Dog);
}

You're unit testing the implementations, then do that. They might share a test implementation if they abide by the same contracts.


Damn, why my mind was so closed and didn't think that? :rolleyes:

You are quite right.
We can extract the common logic to a shared function then call it in different tests, which each test is for one module.

However, I have one more question:
I'm used to, and maybe a lot of people do so, that only write test assertions in the test body (TESTCAT) rather than an external function (testAnimal).
Is there anything bad to forward the test itself (TESTCAT) to another function (testAnimal), like in your code?
I don't think so but want confirmation.

Thanks

http://www.cpgf.org/
cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.
v1.5.5 was released. Now supports tween and timeline for ease animation.


#5 Telastyn   Crossbones+   -  Reputation: 3726

Like
0Likes
Like

Posted 30 November 2011 - 09:23 AM

I'm used to, and maybe a lot of people do so, that only write test assertions in the test body (TESTCAT) rather than an external function (testAnimal).
Is there anything bad to forward the test itself (TESTCAT) to another function (testAnimal), like in your code?


I don't have a ton of experience with teams that do testing, but from what I understand it's frowned upon. Tests should be isolated, and if two classes have the same implementation contract, why don't you have one class?

That said, I've done it for partially shared functionality and it seems better than copy/paste.

#6 wqking   Members   -  Reputation: 756

Like
-1Likes
Like

Posted 30 November 2011 - 09:34 AM

I don't have a ton of experience with teams that do testing, but from what I understand it's frowned upon. Tests should be isolated, and if two classes have the same implementation contract, why don't you have one class?


In my case, the two classes have the same interface, but the implementation is different.
For example, both Cat and Dog supports the same interface canEat and eat, but when, what, and where to eat is the implementation and is different between Cat and Dog.

That said, I've done it for partially shared functionality and it seems better than copy/paste.


Beside copy/paste, I also considered script generated code, include source code, but all of them lack readability and maintenance.
So I may go for the "forward to another function" way.

http://www.cpgf.org/
cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.
v1.5.5 was released. Now supports tween and timeline for ease animation.


#7 Slavik81   Members   -  Reputation: 360

Like
0Likes
Like

Posted 30 November 2011 - 01:22 PM

I think it makes the most sense to have a contract test for the interface provided by the interface module. The Cat test can then supply a Cat to the contract test in its module, and the Dog can supply a Dog to the contract test in its module. When you change the contract test in any way, all derived classes contract tests should automatically have been updated. That is, the contract should come in one piece.

So, the contract for the interface is defined with the interface in the shared module. The check that any derived class conforms to the contract is done in the module of the derived class, as are any tests specific to that derived class.

The specifics of precisely how I'd recommend you do this depends on how your test framework registers tests. If you're using QTest, you could make the contract test abstract and inherit from it in the test for the specific class.

I don't have a ton of experience with teams that do testing, but from what I understand it's frowned upon. Tests should be isolated, and if two classes have the same implementation contract, why don't you have one class?

For example, all classes that inherit from I_Serializer might be required to be able to serialize and deserialize back and forth without changing the object being serialized. You could write this test once and apply it to all things that claim to be a valid implementation of I_Serializer.

They almost certainly have other properties as well. The XmlSerializer would need its own tests that are different from the BinarySerializer. So they could use both a contract test and an implementation test.

#8 Hodgman   Moderators   -  Reputation: 28587

Like
0Likes
Like

Posted 30 November 2011 - 04:50 PM

However, I have one more question:
I'm used to, and maybe a lot of people do so, that only write test assertions in the test body (TESTCAT) rather than an external function (testAnimal).
Is there anything bad to forward the test itself (TESTCAT) to another function (testAnimal), like in your code?
I don't think so but want confirmation.

In my unit testing framework, I just use the engine's ASSERT macro to implement test conditions. If the test calls into other code, and that code uses ASSERTs, then it can trigger a test failure as well.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS