How to spoof C ++ classes containing non-virtual functions?

I am trying to validate legacy C ++ code. Specifically, I have a class hierarchy of, say, A < B < C

(i.e. A

is a subclass B

but B

is a subclass C

) and there is a global reference to an object of the type C

that is used throughout the system code (singleton pattern). The goal is to replace the object C

with some fake object (actually, C

used to access the database).

My first attempt was to introduce interfaces IA, IB, and IC

(which contain pure virtual version of the corresponding class of functions), each class implements the interface and change the type of global reference C

to IC

, in my test setup function, I would replace the object C

bound to the global object, its own implementation IC

, which would make the entire system used by my fake implementation.

However, classes A, B

, and C

contain a lot of no virtual functions. Now, if I make classes inherit from their interfaces, I would change the semantics of these functions from non-virtual to virtual (Pens discusses this issue in Effective Dealing with Legacy Code, p. 367). In other words: I have to check every call to my global object, and I have to make sure that the same functions will still be called after my changes. This sounds like a lot of ERROR PRONE work to me.

I also thought about making the non-virtual functions "final", that is to tell the compiler that the functions A, B

and C

should not be hidden in subclasses (which will make the compiler tell me all the potentially dangerous functions B

and C

- if the function is not hidden in the base class, the above the effect can't happen at all), but that doesn't seem to be supported by C ++ (we don't use C ++ 11 yet, but even its final keyword seems to apply to virtual functions).

To make things even more difficult, the A, B

and classes C

also contain public attributes, virtual functions, and some template functions.

So my question is, how to deal with the situation described above? Are there any C ++ features I missed that might help in my scenario? Any design patterns? Or even refactoring tools? My main requirement is that the changes should be as safe as possible, since the classes I would like to spoof are very important to the system ... I would also be happy with an "ugly" solution that would allow me to deliver tests (and which can be reorganized later if the system is adequately covered by trials).

Edit: I messed up my inheritance hierarchy (it flipped) - this is now fixed.

Edit2: Finally, we ended up like this: we only made the virtual functions that we really need for our current test cases. We then tested each call to these methods (which was managed). This allowed us to mock our class with Google Mocks. With more and more test cases, we hope that our changes will persist over time. Please note that in asking my question, I thought that Google Mocks can only mock clean interfaces; this is not the case, which allows an incremental approach as described above.

+3


source to share


2 answers


I'm going to suggest changing C

to template where the template type is the actual database access / implementation code. Then in your live program, you use C<LiveDatabase>

wherever you are currently using C

, and in testing, you use C<MockDatabase>

. Then A

and B

remain unchanged, and all fragments of the inner work C

remain unchanged, only different database calls differ (in their calls to the delegated implementation).



Next, I would include and work on public attributes, as they can only cause headaches and hard-to-find errors. Just line them up so they are all private and let your compiler tell you they are being accessed. For read-only use, it's super easy to add accessors. Where they mutate, you need to define an appropriate interface for your class in order to achieve the desired mutation. Don't add a mutator method unless it's really your last alternative.

+2


source


EDIT: In the comments on Dietmar KΓΌhl below, this approach won't work to spoof member function templates.



I'm not sure if this will work, but try making a fake class (not virtual) in a separate .cpp file. Then link the tests to the fake class, but not the real one. In theory, the two shojlds have the same ABI (Application Binary Interface), so both should be compatible.

+1


source







All Articles