How to create test objects for legacy third party code

I have a code base where many of the classes that I implement come from classes that are provided by other departments of my company. Working with these other units is often associated with a working relationship, as if they were third-party suppliers of intermediates.

I am trying to write test code without changing these base classes. However, there are problems with creating a meaningful test of objects due to the lack of interfaces:

//ACommonClass.h #include "globalthermonuclearwar.h" //which contains deep #include dependencies... #include "tictactoe.h" //...and need to exist at compile time to get into test... class Something //which may or may not inherit from another class similar to this... { public: virtual void fxn1(void); //which often calls into many other classes, similar to this //... int data1; //will be the only thing I can test against, but is often meaningless without fxn1 implemented //... }; 

Usually I retrieve the interface and work from there, but since it is a "third party", I cannot make these changes.

Currently, I have created a separate file that contains fake implementations for functions defined in third-party headers of the base class, with the need to know the basics, as described in the book "Working with Deprecated Code".

My plan was to continue to use these definitions and provide alternative test implementations for every third class I need:

 //SomethingRequiredImplementations.cpp #include "ACommonClass.h" void CGlobalThermoNuclearWar::Simulate(void) {}; // fake this and all other required functions... // fake implementations for otherwise undefined functions in globalthermonuclearwar.h #include files... void Something::fxn1(void) { data1 = blah(); } //test specific functionality. 

But before I start doing this, I was wondering if anyone had tried to provide actual objects based on code similar to mine, which would allow us to create new test classes to use instead of real third-party classes.

Please note that all the indicated code codes are written in C ++.

+4
source share
5 answers

Layout objects are suitable for this kind of task. They allow you to simulate the existence of other components without requiring their presence. You simply define the expected input and output in your tests.

Google have a good fake for C ++.

+1
source

At the moment I am facing very similar problems. I don’t want to add a bunch of interfaces that exist only for testing, so I can’t use any of the existing mock object libraries. To get around this, I do the same thing by creating another file with fake implementations, and my tests bind fake behavior, and production code binds real behavior.

What I would like to do at this moment is to take the insides of another layout frame and use it inside my fake objects. It would look something like this:

Production.h

 class ConcreteProductionClass { // regular everyday class protected: ConcreteProductionClass(); // I've found the 0 arg constructor useful public: void regularFunction(); // regular function that I want to mock } 

Mock.h

 class MockProductionClass : public ConcreteProductionClass , public ClassThatLetsMeSetExpectations { friend class ConcreteProductionClass; MockTypes membersNeededToSetExpectations; public: MockClass() : ConcreteProductionClass() {} } ConcreteProductionClass::regularFunction() { membersNeededToSetExpectations.PassOrFailTheTest(); } 

ProductionCode.cpp

 void doSomething(ConcreteProductionClass c) { c.regularFunction(); } 

test.cpp

 TEST(myTest) { MockProductionClass m; m.SetExpectationsAndReturnValues(); doSomething(m); ASSERT(m.verify()); } 

The most painful part of all this is that the other layout frameworks are so close to this, but they don’t do it exactly, and the macros are so messy that it’s nontrivial to adapt them. I began to study this in my free time, but he did not move very quickly. Even if my method works the way I want, and I have the code for setting expectations, this method still has a few drawbacks, one of which is that your build commands can be long if you need to link many .o files , not one .a, but it is manageable. It is also impossible to skip to the default implementation, since we are not binding it. In any case, I know that this does not answer the question, or indeed even tell you something that you still do not know, but it shows how close the C ++ community can mock classes that do not have a clean virtual interface .

+1
source

You might want to consider a mockery instead of pretending to be a potential solution. In some cases, you may need to write wrapper classes that are breadboard if the source classes are not. I did this with framework classes in C # /. Net, but not C ++, so YMMV.

0
source

If I have a class that I need for testing, which comes from something that I cannot (or do not want) to run under testing, I:

  • Create a new class for logic only.
  • Move code-i-wanna-test to the logic class.
  • Use the interface to talk to the real class, to interact with the base class and / or things that I cannot or will not insert into the logic.
  • Define a test class using the same interface. This test class could have nothing but a noops or fancy code that mimics real classes.

If I have a class that I just need to use when testing, but using a real class is the problem (dependencies or unwanted behavior):

  • I am defining a new interface similar to all the public methods that I need to call.
  • I will create a mock version of an object that supports this interface for testing.
  • I will create another class that is built with a “real” version of this class. It also supports this interface. The entire interface causes a redirect to the real methods of the object.
  • I will do this only for those methods that I actually call - not ALL public methods. I will add to these classes when I write more tests.

For example, I am migrating MFC GDI classes like this to test Windows GDI drawing code. Templates may make some of them lighter - but we often end up not doing it for various technical reasons (material with exporting Windows DLLs ...).

I am sure that all this is in the Feather Working with Legacy Code book - and what I am describing has real terms. Just don't make me pull the book off the shelf ...

0
source

One thing that you did not mention in your question is the reason why your classes come from base classes from another division. Is the relationship really an IS-A relationship?

If your classes should not be used by the framework, you might consider preferring to delegate by inheritance . Then you can use dependency injection to provide your class with a mock of your class in unit tests.

Otherwise, the idea would be to write a script to extract and create the interface you need from the header that they provide, and integrate it into the compilation process so your unit test can check.

0
source

All Articles