Sign in to follow this  
Followers 0
wqking

Unit test -- test different modules with same interface

7 posts in this topic

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.

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

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

[code]
void testAnimal(Animal *);

MYTEST()
{
testAnimal(new Cat);
testAnimal(new Dog);
}
[/code]

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
-1

Share this post


Link to post
Share on other sites
Here's one suggestion:

[code]void testAnimal(Animal *, const char *name);

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

MYTEST()
{
TEST_ANIMAL(Cat);
TEST_ANIMAL(Dog);
}[/code]

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

Share this post


Link to post
Share on other sites
What prevents this?

[code]
void testAnimal(Animal *);

TESTCAT()
{
testAnimal(new Cat);
}

TESTDOG()
{
testAnimal(new Dog);
}
[/code]

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

Share this post


Link to post
Share on other sites
[quote name='Telastyn' timestamp='1322664600' post='4889076']
What prevents this?

[code]
void testAnimal(Animal *);

TESTCAT()
{
testAnimal(new Cat);
}

TESTDOG()
{
testAnimal(new Dog);
}
[/code]

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

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
-1

Share this post


Link to post
Share on other sites
[quote name='wqking' timestamp='1322665163' post='4889080']
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?
[/quote]

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.
0

Share this post


Link to post
Share on other sites
[quote name='Telastyn' timestamp='1322666585' post='4889087']
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?
[/quote]

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.

[quote name='Telastyn' timestamp='1322666585' post='4889087']
That said, I've done it for partially shared functionality and it seems better than copy/paste.
[/quote]

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.
-1

Share this post


Link to post
Share on other sites
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.

[quote=Telastyn]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?[/quote]
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.
0

Share this post


Link to post
Share on other sites
[quote name='wqking' timestamp='1322665163' post='4889080']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.[/quote]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.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0