Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing smart wrappers for 3rd party libraries

Suppose you are required to use an unnecessarily complicated, difficult to mock (perhaps it has concrete classes with no virtual interface), and unreliable third-party library that integrates with some external resource such as a socket or a database. You decide to create "wrapper" interfaces/classes to greatly simplify the usage of this library and to allow developers using the wrapper to continue to write testable code. The wrapper's interface looks nothing like the original interface.

I have a few questions about how to test this wrapper.

  1. Should the wrapper be tested without the external resource by developing a method-for-method layer over the bad library that can be mocked?

  2. When you test your wrapper classes with the 3rd party library (using the external resources) is this a unit test or an integration test? If the external resource can be embedded in memory during the automated test, is it still an integration test?

  3. At what point do we quit mocking and stubbing and say that we have a unit. According to wikipedia "A unit is the smallest testable part of an application." but I am finding this difficult to measure. If speed is a factor in decide whether or not we are testing a unit, how do you decide how slow is too slow for the test to be called a unit test?

like image 520
insipid Avatar asked May 07 '11 21:05

insipid


2 Answers

TDD doesn't say that everything must be unit tested. TDD says that you should write a test first but it doesn't have to be a unit test.

  1. Start with integration test - it will test your logic dependent on wrapper which will communicate with a real component. No mocks here. It is integration test because it tests multiple layers of your application and real component still uses sockets or database access.
  2. The integration test will fail because you don't have your logic
  3. Write unit test with mocked wrapper to test the logic
  4. The unit test will fail because you don't have your logic
  5. Write the logic to satisfy the unit test (4.)
  6. Repeat 3.-5. to get all logic necessary to satisfy the integration test (1.)
  7. Repeat whole process with the next integration test

There is no need to write unit tests for wrapper. The main wrapper's function is to wrap the component. If you write unit test for wrapper you will test that it calls method on the component but in such case you are back at the beginning - how to mock the component? If you write integration test just for wrapper calling the component you are retesting the component (OK this is sometimes handy but in normal scenario you don't do that).

I recommend reading Growing Object-Oriented Software guided by tests by Steve Freeman and Nat Pryce.

like image 167
Ladislav Mrnka Avatar answered Nov 15 '22 09:11

Ladislav Mrnka


I think the this question revolves around this statement:

The wrapper's interface looks nothing like the original interface

This likely indicates that there's a significant amount of logic involved in the translation between the wrapper and the original interface. That sounds a lot like an anti-corruption layer, and if that logic is complex, it ought to be tested.

The best way to do that is still to extract a 1:1 interface from the original API. However, this is not the interface you expose to the rest of the application. The interface you expose to the rest of the application can be a Facade over the extracted interface. In a sense you could say that the extracted interface is an implementation detail of the anti-corruption layer and not something that's exposed to the rest of the application.

This enables you to unit test the translation between the Facade interface and the extracted interface while still keeping the original, difficult-to-test component out of the test.

What remains is the translation between the extracted interface and the original component. However, if that interface was extracted as a 1:1 mapping of the original component, the implementation should consist of pure delegation. In other words, the implementation would have a cyclomatic complexity of 1 and thus be a Humble Object that need not be unit tested.

You might still want to throw a few integration or system tests at the completed system, but these may take the role of smoke tests because you should already have sufficient coverage from the unit tests.

like image 33
Mark Seemann Avatar answered Nov 15 '22 10:11

Mark Seemann