1

Suppose I have the following classes from a third-party library:

public class ThirdPartyType { ... }

public class ThirdPartyFunction
{
    public ThirdPartyType DoSomething() { ... }
}

The implementation details are not important, and they are actually outside of my control for this third-party library.

Suppose I write an adapter class for ThirdPartyFunction:

public class Adapter
{
    private readonly ThirdPartyFunction f;

    public Adapter()
    {
        f = new ThirdPartyFunction();
    }

    public string DoSomething()
    {
        var result = f.DoSomething();

        // Convert to a type that my clients can understand
        return Convert(result);
    }

    private string Convert(ThirdPartyType value)
    {
        // Complex conversion from ThirdPartyType to string
        // (how do I test this private method?)
        ...
    }
}

How can I test that my implementation of Convert(ThirdPartyType) is correct? It's only needed by the Adapter class, which is why it's a private method.

redcurry
  • 2,381
  • 2
  • 24
  • 38
  • 1
    Verifying DoSomething, if done correctly should verify Convert... You usually do not verify private methods, but if you mark it internal you could expose it to a testing library. – Ron Beyer Dec 19 '19 at 22:36
  • @RonBeyer I agree that verifying DoSomething would take care of Convert. However, the third-party library has been tested very thoroughly (assume thousands of tests), and I don't want to repeat all those tests again. The "internal" idea could work. – redcurry Dec 19 '19 at 22:40

2 Answers2

4

I would recommend extracting the code to a seprate class and then testing that class. Although it is only used by this Adapter it shouldn't be the responsibility of the adapter to do the conversion as well (in keeping with the Single Responsibility Principle).

By extracting it out it gives you the ability to test the converter in isolation from the third party code.

If the converter does not require any state you could also make it a static class and then reference it directly in your adapter without the need for registering it with dependency injection.

If you think about it, the adapter doesn't need testing (as it is just a wrapper) but the converter does - so extracting it to another class makes sense to allow it to be tested, even if it does mean another class in the code.

In addition, extracting the converter to a separate class means that if the ThirdPartyType or the format of your string changes then you can make the changes without affecting the Adapter implementation.

Simply Ged
  • 8,250
  • 11
  • 32
  • 40
1

If you change your Adapter class to allow the ThirdPartyFunction f to be passed to it, then you can use a mock version of it in your test class. this will allow you to test the Convert function.

I'm not sure of the exact syntax as I am not familiar with the language, but I will give it a try:

public class Adapter
{
  private readonly ThirdPartyFunction f;

  // pass the function to the constructor when creating the adapter
  public Adapter(ThirdPartyFunction inputF)
    {
      f = inputF;
    }

  public string DoSomething()
  {
    var result = f.DoSomething();

    // Convert to a type that my clients can understand
    return Convert(result);
  }

  private string Convert(ThirdPartyType value)
  {
    // Complex conversion from ThirdPartyType to string
    // (how do I test this private method?)
    ...
  }
}

In your real implementation, when you create the new Adapter, you can pass it a new ThirdPartyFunction.

In your test implementation, you can pass it a Mocked version of that ThirdPartyFunction which returns a fixed value for testing. Maybe like:

class MockThirdPartyFunction extends ThirdPartyFunction
{
  private ThirdPartyType testData;

  public MockThirdPartyFunction(ThirdPartyType data)
  {
    testData = data;
  }

  override public ThirdPartyType DoSomething() 
  {
    // return a fixed third party type passed in on mock creation
    return testData
  }
}

You create the test adapter with the mocked value, where you can set the specific ThirdPartyType you want to test. Then in your test when you call DoSomething on the Adapter, you are controlling the input to your Convert function and can see the output and compare accordingly to expected results.

Andy
  • 4,441
  • 1
  • 19
  • 16