4
votes

Comment se moquer d'un objet hérité dans ce code ci-dessous?

J'ai écrit des tests unitaires en utilisant le framework mockito. J'ai ce code hérité ci-dessous, comment puis-je simuler l'objet client RemoteService dans la méthode approbationAction sans le refactoriser?

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;
}


7 commentaires

pourquoi changeriez-vous le code de votre logique métier pour simuler un service dans un test?


@Stultuske, Dans certains cas: lors de l'écriture de tests unitaires et de la couverture de scénarios de test, nous pouvons trouver des points problématiques dans la logique métier. Ainsi, il peut être raisonnable de refactoriser le code tout en considérant les cas de test. De bons tests unitaires contribuent à améliorer l'architecture


Pas facilement. Pourquoi incluez-vous la contrainte "sans refactoring"?


@AkinerAlkan si vous optez pour le développement piloté par les tests, vos tests sont écrits avant votre implémentation. cela étant dit, nous ne parlons pas de code qui est (par des tests démontrés) défectueux, nous parlons de changer le code métier pour permettre aux tests de s'exécuter.


RemoteService doit être enveloppé derrière une interface et utilisé via cette interface. Ensuite, vous pouvez écrire des tests unitaires via la maquette de cette interface IRemoteService


Une solution laide serait de rendre le package getRemoteService privé, puis d'espionner la classe que vous testez et de la simuler.


Pourquoi ne pas utiliser PowerMock pour se moquer du RemoteService ? PowerMockito pour être précis dans ce cas.


3 Réponses :


0
votes

remplacez getRemoteService par protected et utilisez la sous-classe pour la remplacer:

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();
    }
}


0 commentaires

0
votes

Vous ne pouvez pas vous moquer de RemoteService car il est créé dans votre classe. Si possible, extrayez getRemoteService vers une autre classe, appelons-le NewClass. Vous pouvez maintenant vous moquer de NewClass.

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

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

Si vous ne voulez pas beaucoup changer votre code hérité, vous pouvez aussi étendre votre classe (qui implémente approbationAction) remplacez la fonction getRemoteService (). Dans la nouvelle implémentation de getRemoteService, appelez create simuler RemoteService. Vous pouvez alors tester cette nouvelle classe.


0 commentaires

1
votes

Pourquoi ne pas rendre le RemoteService injectable via le constructeur, mais aussi autoriser un constructeur par défaut?

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

Bien sûr, vous devrez adapter vos constructeurs déjà existants. mais comme vous n’avez pas montré toute la classe, c’est impossible pour moi.

Cela vous laisse avec une seule instance d'un client pendant toute la durée de vie de YourClass.


Il est maintenant très facile de se moquer du RemoteService . Instanciez simplement YourClass avec le client fictif:

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

}


0 commentaires