📜 ⬆️ ⬇️

Mocks, fakes and stubs in C ++

The translation presents a new approach to unit testing of a huge base of legacy C ++ code that reacts poorly to tests.



We pay a lot of attention to testing - partly to make life easier for ourselves, partly to provide developers with a stable and reliable Unity engine. Therefore, we continue to continuously optimize this process.

We already wrote about high-level testing in C #, but over time we came to the conclusion that we should not forget about lower-level, faster and more accurate tests. The problem is that our code base reacts very badly to them. Testing a huge base of legacy C ++ code is no easy task. But we still managed to simplify it.
')
After reviewing the existing solutions, we found out that none of them do not suit us (we were looking for something like Typemock products, but they only work under Windows, and therefore we don’t suit us), and set a goal to solve the problem on our own. Of course, it was pointless to rewrite the entire array of code just for the sake of testing. We needed a solution compatible with our C ++ code design.

Intercept…

We realized that it would be nice to be able to intercept functions at any stage of testing and make mock objects, fakes or stubs of them. We are satisfied that the function X directly calls the function Y, provided that the function Y can be intercepted and turned into anything.

Then we thought: you need to patch the code! Everything pointed to the need to replace the code in real time for profiling. But there was a catch: many platforms do not support the ability to change the code at runtime.

... at compile time ...

We did not despair: in the end, patch can be at compile time! It turned out that here is not so simple. This process has already been successfully implemented, for example by Bullseye, whose products we use to test code coverage. But he is still too confused. We use a lot of compilers for different platforms, and our builds are more complicated than we would like, so we also had to give up this option.

... using macros

It's good that in C ++ there is always a fallback - templates and macros. So, I made myself comfortable and wrote a couple of templates, diluting them with macros.
The idea is quite simple: on the basis of the mechanism for intercepting functions, you can build a whole framework that changes functions in accordance with the purpose of testing.

Step one: interceptors.



It's simple. By default, this code does nothing (and is not compiled into anything in the ready builds of the engine), but during testing it works with a bang. The process is a bit intrusive, but quite a bit.

Next step: make the interceptor run during the test.



This code will search for an interceptor based on the signature of the function, provided that the fake is in scope.
Finally, you can write the interception mechanism itself:



Now you can proceed to the usual layout manipulations: redirect calls to your own function, take arguments, return values ​​and manage the execution of the original function. This system not only supports class methods and free functions, but also allows you to replace whole classes.



Conclusion

Although we still have to find out how effective this approach is, we can already write new tests that we could not write before. We are not going to forget about functional testing and we hope that thanks to our own set of fast, comprehensive and detailed tests, we will ensure even more stable operation of the Unity engine.

Source: https://habr.com/ru/post/275521/


All Articles