How do I mock a legacy object in this code below?

Viewed 99

I have been writing unit tests using mockito framework. I have this below legacy code, how do I mock the RemoteService client object in the approvalAction method without refactoring it?

public Map<String, String> approvalAction(long documentId, ActionCommandDTO request, FormData formData, byte[] prevData) {

    RemoteService client = getRemoteService();
    String urlString = String.format("formExtensions/%s?%s", formData.getId(), getAuthParam(formData.getRealm()));
    try {
        response = client.postEntity(urlString, String.class, approvalSvcRequestStr);
    } catch (Exception e) {
        // TODO: handle rollback properly for P2P
        handleApprovalActionFailed(documentId, request, formData, prevData);
    }

    return map;
}


private RemoteService getRemoteService() {

    RemoteServiceConfig remoteServiceConfig = (RemoteServiceConfig) this.serviceConfigRegistry.getServiceConfigs().get("approval");
    remoteServiceConfig.setClientID(clientId);
    remoteServiceConfig.setClientSecret(privateSecret);
    RemoteService remoteService = new RemoteService(remoteServiceConfig, authorizationHeaderServiceImpl);

    return remoteService;
}
3 Answers

Why not make the RemoteService injectable via the constructor, but also allow a default constructor?

class YourClass{

    private final RemoteService client;

    public YourClass(RemoteService client){
        this.client = client;
    }

    public YourClass(){
        RemoteServiceConfig remoteServiceConfig = (RemoteServiceConfig) this.serviceConfigRegistry.getServiceConfigs().get("approval");
        remoteServiceConfig.setClientID(clientId);
        remoteServiceConfig.setClientSecret(privateSecret);
        client = new RemoteService(remoteServiceConfig, authorizationHeaderServiceImpl);
    }

    // all of your other methods inside the class

}

Of course you'd have to adapt your already existing constructors. but as you've not shown the whole class this is impossible for me.

This leaves you with a single instance of a client throught the whole lifetime of YourClass.


Now it's very easy to mock the the RemoteService. Just instantiate YourClass with the mocked client:

RemoteService mock = mock(RemoteService.class);
YourClass toTest = new YourClass(mock);

change getRemoteService to protected, and use subclass to override it:

public class SomeService {

    public Map<String, String> approvalAction(long documentId, ActionCommandDTO request, FormData formData, byte[] prevData) {

    }


    // change here
    protected RemoteService getRemoteService() {

        RemoteServiceConfig remoteServiceConfig = (RemoteServiceConfig) this.serviceConfigRegistry.getServiceConfigs()
                .get("approval");
        remoteServiceConfig.setClientID(clientId);
        remoteServiceConfig.setClientSecret(privateSecret);
        RemoteService remoteService = new RemoteService(remoteServiceConfig, authorizationHeaderServiceImpl);

        return remoteService;
    }

}


public class TestSomeService extends SomeService {


    @Override
    protected RemoteService getRemoteService() {
        return new MockService();
    }
}

You cannot mock RemoteService because it's getting created inside your class. If possible extract getRemoteService to another class lets call it NewClass. Now you can mock NewClass.

public Map<String, String> approvalAction(long documentId, ActionCommandDTO request, FormData formData, byte[] prevData) {

    RemoteService client = NewClass.getRemoteService(params);
    ///
}

Other way is if you don't want to change your legacy code much is to extend your class(which implements approvalAction) override getRemoteService() function. In new implementation of getRemoteService call create mock RemoteService. You can than test this new class.

Related