简体   繁体   中英

How to handle 3rd Party Dependencies while Unit Testing

I am developing a plugin for a proprietary application.

The specification essentially is that I have functions with specified names. When installed, the parent application will call the functions in my plugin - passing in various parameters.

I want to unit test my plugin, but I don't have access to the source of the parent application. I can't instantiate the required parameters to test my function.

In this particular case, my parameter is an object with two data elements I access and a logging function that I access. Mocking up a sample wouldn't be too difficult, but I'm faced with a bit of a dilemma...

public PerSpecResponseObject myPluginFunction(ThirdPartyObject iUseThis){
...
}

I can't pass my own object into "myPluginFunction", I need something of the type ThirdPartyObject. If I define an interface, I don't have access to the ThirdPartyObject to specify that it implements the interface. I can't subclass ThirdPartyObject and use a generic parameter ( <? extends ThirdPartyObject> ), because my plugin is implementing an interface, so my parameter types are restricted. I looked into Mocking and while interesting, it didn't seem applicable for my situation.

What solutions exist for this situation?

If you had access to the source of the parent application, you wouldn't be unit-testing, but integration-testing.

You can mock ThirdPartyObject . In fact, you have to mock ThirdPartyObject if what you want to do is unit-testing.

Just create a class with the same FQN as ThirdPartyObject , but keep it in your test folders so it doesn't get distributed.

This is the simplest I can think of.

It would indeed be best to be able to construct a real ThirdPartyObject .

Since you need to refer to this class, you have at least some 3rd party library on your class path that contains this class. Are you sure there's no way to construct it, eg using a factory which is also in the library? Or by constructing another object and calling a method that will call your plugin with a ThirdPartyObject instance?

While this is sometimes called integration testing (since you're testing the integration with the main application), it can also be considured unit testing in the classical sense as long as the test doesn't eg put data in a database or does anything else that could potentially influence other tests.

If the above isn't possible , you may resort to mocking out ThirdPartyObject eg using Mockito . Try to make sure you your test code isn't coupled to your implementation any more than it needs to be. Some people think they need to mock out all dependencies of a class and then verify all method calls to those dependencies. They're introducing a lot of strong coupling and redundancy.

Concerning the 2 problems you mentioned, there are ways around both:

1) You say you can't make ThirdPartyObject implement an interface. That's true, but you can write an adapter that implements this interface and delegates to a ThirdPartyObject . Then the method called by the main application would simply delegate to the actual plugin method implementation that uses this interface.

Example (suppose ThirdPartyObject has a single method void thirdPartyMethodCall() :

public interface InterfaceForThirdPartyObject {
    void thirdPartyMethodCall();
}

public class ThirdPartyObjectAdapter implements InterfaceForThirdPartyObject {
    private final ThirdPartyObject delegate;

    public ThirdPartyObjectAdapter(ThirdPartyObject delegate) {
        this.delegate = delegate;
    }

    public void thirdPartyMethodCall() {
        delegate.thirdPartyMethodCall();
    }
}

// your actual plugin implementation, not directly exposed to the main app
public class MyPlugin {
    public PerSpecResponseObject myPluginFunction(InterfaceForThirdPartyObject iUseThis){
        // this will contain your actual logic that you want to test
    }
}

// this is the plugin as exposed to the main app
public class MyPluginAdapter implements PluginInterface {
    private final MyPlugin delegate = new MyPlugin();

    // this is called by the main application
    public PerSpecResponseObject myPluginFunction(ThirdPartyObject iUseThis) {
        delegate.myPluginFunction(new ThirdPartyObjectAdapter(iUseThis));
    }
}

2) You say you can't subclass ThirdPartyObject because the plugin implements an interface that has a ThirdPartyObject as method argument instead of ? extends ThirdPartyObject ? extends ThirdPartyObject . I don't see why that would be a problem: a method that takes a ThirdPartyObject as parameter will happily take instances of any subclass of ThirdPartyObject .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM