15

Like the author of this question from 2012 and this one from 2013, I have a 3rd party library that I need to wrap in order to properly test my application. The top answer states:

You always want to wrap third party types and methods behind an interface. This can be tedious and painful. Sometimes you can write a code generator or use a tool to do this.

In my case, the library is for an object model and consequently has a larger number of classes and methods that would need to be wrapped for this strategy to be successful. Beyond merely "tedious and painful", this becomes a hard barrier to testing.

In the 4 years since this question, I'm aware that isolation frameworks have come a long way. My question is: does there now exist a simpler way to achieve the effect of full wrapping of 3rd party libraries? How can I take the pain out of this process and reduce the manual effort?


My question is not a duplicate of the questions I initially linked to, since my one is about reducing the manual effort of wrapping. Those other questions only ask if wrapping make sense, not how the effort can be kept small.

Tom Wright
  • 401
  • 2
  • 12

4 Answers4

10

Don't unit test that code. Write integration tests instead. In some cases, unit testing, mocking, is tedious and painful. Ditch the unit tests and write integration tests that actually make vendor call outs.

Note, these tests should be run after the deployment as a post deployment automated activity. They are not run as part of unit tests or part of the build process.

Sometimes an integration test is a better fit than a unit test in certain parts of your application. The hoops one must go through to make the code "testable" can sometimes be detrimental.

Jon Raynor
  • 10,905
  • 29
  • 47
  • 2
    First thing I thought when I read your answer was "unrealistic for PDF, since comparing the files on a binary level does not tell you what changed or if the change is problematic". Then I found [this older SO post](http://stackoverflow.com/questions/145657/tool-to-compare-large-numbers-of-pdf-files), indicates it actually might work (so +1). – Doc Brown Dec 13 '16 at 12:43
  • @DocBrown the tool in the SO link doesn't compare the PDF's in a binary level. It compares the structure and the contents of the pages. Pdf internal structure : https://www.safaribooksonline.com/library/view/pdf-explained/9781449321581/httpatomoreillycomsourceoreillyimages952073.png – linuxunil Dec 20 '16 at 11:08
  • 1
    @linuxunil: yes, I know, as I wrote, **first** I thought ... but **then** I found the solution mentioned above. – Doc Brown Dec 20 '16 at 11:33
  • oh.. i see.. maybe the 'it actually might work' in the final of your answer got me confused. – linuxunil Dec 20 '16 at 13:34
4

Assuming you're not looking for a mocking framework, because they're ultra-ubiquitous and easy to find, there are a few things worth noting up front:

  1. There is "never" anything you should "always" do.
    It's not always best to wrap up a third party library. If your application is intrinsically dependent on a library, or if it's literally built around one or two core libraries, don't waste your time wrapping it up. If the libraries change, your application will need to change anyway.
  2. It is OK to use integration tests.
    This is especially true around boundaries that are stable, intrinsic to your application, or cannot be easily mocked. If those conditions are met, wrapping and mocking will be complicated and tedious. In that case, I'd avoid both: don't wrap and don't mock; just write integration tests. (If automated testing is a goal.)
  3. Tools and framework cannot eliminate logical complexity.
    In principle, a tool can only cut down on boilerplate. But, there's no automate-able algorithm for taking a complex interface and making it simple -- let alone taking interface X and adapting it to suit your needs. (Only you know that algorithm!) So, while there are undoubtedly tools that can generate thin wrappers, I'd suggest that they're not already ubiquitous because, in the end, you still need to just code intelligently, and therefore manually, against the interface even if it's hidden behind a wrapper.

That said, there are tactics you can use in many languages to avoid referring directly to a class. And in some cases, you can "feign" an interface or thin wrapper that doesn't actually exist. In C#, for example, I would go one of two routes:

  1. Use a factory and implicit typing.

You can avoid the effort of fully wrapping a complex class with this little combo:

// "factory"
class PdfDocumentFactory {
  public static ExternalPDFLibraryDocument Build() {
    return new ExternalPDFLibraryDocument();
  }
}

// code that uses the factory.
class CoreBusinessEntity {
  public void DoImportantThings() {
    var doc = PdfDocumentFactory.Build();

    // ... i have no idea what your lib does, so, I'm making stuff but.
    // but, you can do whatever you want here without explicitly
    // referring to the library's actual types.
    doc.addHeader("Wee");
    doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return doc.exportBinaryStreamOrSomething();
  }
}

If you can avoid storing these objects as members, either through a more "functional" approach or by storing them in a dictionary (or whatever), this approach has the benefit of compile-time type checking without your core business entities needing to know exactly what class they're working with.

All that is required is that, at compile time, the class returned by your factory actually has the methods on it that your business object is using.

  1. Use dynamic typing.

This is in the same vein as using implicit typing, but involves another tradeoff: You lose compile-type checks and gain the ability to anonymously add external dependencies as class members and inject your dependencies.

class CoreBusinessEntity {
  dynamic Doc;

  public void InjectDoc(dynamic Doc) {
    Doc = doc;
  }

  public void DoImortantThings() {
    Doc.addHeader("Wee");
    Doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return Doc.exportBinaryStreamOrSomething();
  }
}

With both of these tactics, when it comes time to mock ExternalPDFLibraryDocument, as I stated earlier, you have some work to do -- but, it's work you'd need to do anyway. And, with this construction, you've avoided tediously defining 100's of thin little wrapper classes. You've simply used the library without looking directly at it -- for the most part.

With all of that said, there are three big reasons I would still consider explicitly wrapping up a third party library -- none of which would hint at using a tool or framework:

  1. The specific library is not intrinsic to the application.
  2. It would be very expensive to swap without wrapping it up.
  3. I don't like the API itself.

If I don't have some level concern in all three of those areas, you don't make any significant effort to wrap it up. And, if you have some concern in all three areas, an auto-generated thin wrapper isn't really going to help.

If you've decided to wrap a library up, the most efficient and effective use of your time is to build your application against the interface you want; not against an existing API.

Put another way, heed the classical advice: Postpone every decision you can. Build the "core" of your application first. Code against interfaces that will eventually do what you want, which will eventually be fulfilled by "peripheral stuff" that doesn't exist yet. Bridge the gaps as-needed.

This effort may not feel like time-saved; but if you feel you need a wrapper, this is the most efficient way to do it safely.

Think of it this way.

You need to code against this library in some dark corner of your code -- even if it's wrapped up. If you mock the library during testing, there's unavoidable manual effort there -- even if it's wrapped up. But, that doesn't mean you need to directly acknowledge that library by name throughout the bulk of your application.

TLDR

If the library is worth wrapping, use tactics to avoid widespread, direct references to your 3rd party library, but don't take shortcuts to generate thin wrappers. Build your business logic first, be deliberate about your interfaces, and emerge your adapters organically, as needed.

And, if it comes to it, don't be afraid of integration tests. They're a little fuzzier, but they still offer evidence of working code, and they can still easily be made to keep regressions at bay.

svidgen
  • 13,414
  • 2
  • 34
  • 60
  • 2
    This is another post not answering the question - which was explicitly **not** "when to wrap", but "how to reduce the manual effort". – Doc Brown Dec 19 '16 at 22:47
  • @DocBrown Do my edits make my point any more clear? – svidgen Dec 19 '16 at 23:04
  • 1
    Sorry, but I think you missed the core point of the question. I don't see how your suggestions could reduce any **manual** effort in wrapping. Assume the lib in stake has a complex object model as API, with some dozens classes and hundreds of methods. Its API is fine as it is for standard usage, but how to wrap it for unit testing with less pain/effort? – Doc Brown Dec 20 '16 at 05:43
  • The only thing I see in your answer is you do not know a way to do this with reduced effort, and so suggest just not to invest the effort except for specific reasons which justify it. This is what John Raynor already suggested, and exacty what I always did in the past because I do not know a better approach. But what bothers me here actually is: are there newer approaches beyond my "old wisdom" which make it feasible to wrap complex third party libs with less effort, making it feasible to wrap for more "use cases" than the three you mentioned? – Doc Brown Dec 20 '16 at 05:57
  • 1
    TLDR; if you want the bonus points, tell us something we don't know already ;-) – Doc Brown Dec 20 '16 at 05:59
  • 1
    @DocBrown I didn't miss the point. But, I think *you* missed the point of my answer. Investing the *right* effort saves you a lot of work. There are testing implications -- but that's just a side effect. Using a tool to automagically generate a thin wrapper around a library still leaves you building your core library around someone else's API and building mocks -- which *is* a lot of effort that *can't* be avoided if you're insistent upon leaving the library out of your tests. – svidgen Dec 20 '16 at 14:58
  • @DocBrown Furthermore, if you really think this question is about what **tools** to use, a veteran site user like yourself should be voting to close it; not *putting a bounty on it:* http://meta.softwareengineering.stackexchange.com/questions/6483/why-was-my-question-closed-or-down-voted/6487#6487 – svidgen Dec 20 '16 at 14:59
  • This question is not a request for a *specific* tool, as we already discussed [here](http://meta.softwareengineering.stackexchange.com/questions/8353/can-this-edited-question-be-reopened) - it is more "does for this class of problems a class of tools or approaches exist?"- which is IMHO on-topic. If your answer is "definitely no, there is no such class, only thing we can do is mitigate the problem by X,Y and Z", then you should put that more clearly in the focus of your answer. – Doc Brown Dec 20 '16 at 15:12
  • 1
    @DocBrown This is absurd. "Does this class of problems have a class of tools or approaches?" Yes. Of course it does. **Defensive coding** and **not getting dogmatic about unit tests** ... like my answer says. If you *did* have an automagically generated thin wrapper, what value would it provide!? ... It wouldn't permit you to inject dependencies for testing, you still have to do that manually. And it wouldn't allow you to swap the library out, because you're *still coding against the library's API*... it's just indirect now. – svidgen Dec 20 '16 at 15:29
  • If you know for sure there are no isolation frameworks (that is what the question text is asking for), or you can provide a good explanation why one cannot build such a thing for a clear reason, then *this* might be an answer to the question. – Doc Brown Dec 20 '16 at 15:49
  • @DocBrown I don't know that there are no isolation frameworks. The point is, you need to code against the library's interface in some form or fashion *no matter what.* There's a high level of manual effort in coding against a complex API that simply cannot be avoided. I will try to clarify my answer. And, I'm pretty confident it's the "right" answer ... even if I don't expect you'll *like* it. – svidgen Dec 20 '16 at 15:54
  • 1
    Don't get me wrong, I agree with all you wrote in your answer. It just does not match the actual question. If you can proof, or at least give some good arguments why auto-wrapping or semi-automatic wrapping does not work, be sure I will like your answer. – Doc Brown Dec 20 '16 at 16:03
  • @DocBrown **Edited.** I look forward to your next round of criticism. :) – svidgen Dec 20 '16 at 17:21
  • Well, I read it, and I think I like it - though it seems it has become a little bit long-winded, don't you think so? ;-). +1 But let us see what we get in the next 6 days. – Doc Brown Dec 20 '16 at 17:54
  • @DocBrown I appreciate that -- and the effort you put into your criticisms. There's no shortage of totally unexplained downvoting around here, so it's pretty great when someone takes the time to actually help improve an answer. (Especially when the author is stubborn and thick-headed!) So .. Thanks! – svidgen Dec 20 '16 at 17:56
  • @DocBrown Yeah. As the adage says, *I'd have written less, but I didn't have time.* – svidgen Dec 20 '16 at 17:58
  • Thanks @svidgen for this answer. I think this is one of those times where the journey has been as significant as the destination - the conversation around the question has been interesting in it's own right. I've read through your answer a couple of times now and there's lots I can apply. Thanks again. – Tom Wright Jan 03 '17 at 08:46
1

As I understand, this discussion focuses on opportunities for wrapper creation automation rather than wrapping idea and implementation guideline. I will try to abstract from the idea as there is already a lot about it here.

I see that we are playing around .NET technologies, therefore we have powerful reflection capabilities in our hands. You may consider:

  1. Tool like .NET Wrapper Class Generator. I haven't used that tool and I know that it operates on a legacy technology stack, but maybe for your case it would fit. Of course, code quality, support for Dependency Inversion and Interfaces Segregation should be investigated separately. Maybe there are other tools like that but I didn't search a lot.
  2. Writing your own tool which will search in referenced assembly for public methods / interfaces and does the mapping and code generation. Contribution to the community would be more than welcome!
  3. If .NET is not a case ... maybe look here.

I believe that such automation may constitute a base point, but not a final solution. Manual code refactoring will be required ... or in worst case a redesign as I completely agree with what svidgen wrote:

Build your application against the interface you want; not against a 3rd party API.

tom3k
  • 77
  • 4
  • Ok, at least an idea how the problem *might* be handled. But not based on own experience for this use case, I assume? – Doc Brown Dec 20 '16 at 11:48
  • The question is based on my own experience regarding software engineering domain (wrapper, reflection, di)! Regarding the tools - I didn't used that as stated in the asnwer. – tom3k Dec 20 '16 at 12:11
0

Follow these guidelines when creating wrapper libraries:

  • expose only a small subset of the third party library that you need right now (and expand on demand)
  • keep the wrapper as thin as possible (no logic belongs there).
Rumen Georgiev
  • 678
  • 4
  • 9