Test REST avec plusieurs types MIME

Test de REST avec plusieurs types MIME

1. Vue d'ensemble

Cet article se concentrera sur le test d'un service REST avec plusieurs types de médias / représentations.

Nous écrirons des tests d'intégration capables de basculer entre les multiples types de représentations pris en charge par l'API. L'objectif est de pouvoir exécuter exactement le même test en utilisant les mêmes URI du service, en demandant simplement un type de support différent.

2. Buts

Toute API REST doit exposer ses ressources en tant que représentations utilisant un ou plusieurs types de média. The client will set the Accept header to choose the type of representation it asks for from the service.

Étant donné que la ressource peut avoir plusieurs représentations, le serveur devra implémenter un mécanisme responsable du choix de la bonne représentation. Ceci est également connu sous le nom de négociation de contenu.

Ainsi, si le client demandeapplication/xml, alors il devrait obtenir une représentation XML de la ressource. Et s'il demandeapplication/json, alors il devrait obtenir JSON.

3. Infrastructure de test

Nous allons commencer par définir une interface simple pour un marshaller. Ce sera l'abstraction principale qui permettra au test de basculer entre différents types de média:

public interface IMarshaller {
    ...
    String getMime();
}

Ensuite, nous avons besoin d’un moyen d’initialiser le bon marshaller en fonction d’une forme de configuration externe.

For this, we’ll use a Spring FactoryBean to initialize the marshaller and a simple property to determine which marshaller to use:

@Component
@Profile("test")
public class TestMarshallerFactory implements FactoryBean {

    @Autowired
    private Environment env;

    public IMarshaller getObject() {
        String testMime = env.getProperty("test.mime");
        if (testMime != null) {
            switch (testMime) {
            case "json":
                return new JacksonMarshaller();
            case "xml":
                return new XStreamMarshaller();
            default:
                throw new IllegalStateException();
            }
        }

        return new JacksonMarshaller();
    }

    public Class getObjectType() {
        return IMarshaller.class;
    }

    public boolean isSingleton() {
        return true;
    }
}

Regardons ceci:

  • tout d'abord, la nouvelle abstraction deEnvironment introduite dans Spring 3.1 est utilisée ici - pour en savoir plus, consultez lesdetailed article on using Properties with Spring

  • nous récupérons la propriététest.mime de l'environnement et l'utilisons pour déterminer quel marshaller créer - une syntaxe Java 7switch on String à l'œuvre ici

  • ensuite, le marshaller par défaut, au cas où la propriété ne serait pas définie du tout, sera le marshaller Jackson pour le support JSON

  • enfin - ceBeanFactory n'est actif que dans un scénario de test, car nous utilisons le support@Profile, également introduit dans Spring 3.1

C'est tout - le mécanisme est capable de basculer entre les marshallers en fonction de la valeur de la propriététest.mime.

4. Les Marshallers JSON et XML

Nous aurons besoin de l’implémentation réelle du marshaller - une pour chaque type de support pris en charge.

Pour JSON, nous utiliseronsJackson comme bibliothèque sous-jacente:

public class JacksonMarshaller implements IMarshaller {
    private ObjectMapper objectMapper;

    public JacksonMarshaller() {
        super();
        objectMapper = new ObjectMapper();
    }

    ...

    @Override
    public String getMime() {
        return MediaType.APPLICATION_JSON.toString();
    }
}

Pour le support XML, le marshaller utiliseXStream:

public class XStreamMarshaller implements IMarshaller {
    private XStream xstream;

    public XStreamMarshaller() {
        super();
        xstream = new XStream();
    }

    ...

    public String getMime() {
        return MediaType.APPLICATION_XML.toString();
    }
}

Note that these marshallers are not Spring beans themselves. La raison en est qu’ils seront amorcés dans le contexte Spring par lesTestMarshallerFactory;, il n’est pas nécessaire de les transformer directement en composants.

5. Consommer le service avec JSON et XML

À ce stade, nous devrions pouvoir exécuter un test d'intégration complet avec le service déployé. L'utilisation du marshaller est simple: nous allons injecter unIMarshaller dans le test:

@ActiveProfiles({ "test" })
public abstract class SomeRestLiveTest {

    @Autowired
    private IMarshaller marshaller;

    // tests
    ...

}

Spring décidera du marshaller exact à injecter en fonction de la valeur de la propriététest.mime.

Si nous ne fournissons pas de valeur pour cette propriété, lesTestMarshallerFactory se rabattront simplement sur le marshaller par défaut - le marshaller JSON.

6. Maven et Jenkins

Si Maven est configuré pour exécuter des tests d'intégration sur un service REST déjà déployé, vous pouvez l'exécuter en utilisant:

mvn test -Dtest.mime=xml

Ou, si c'est la construction utilise la phaseintegration-test du cycle de vie Maven:

mvn integration-test -Dtest.mime=xml

Pour plus de détails sur la configuration de la version Maven pour exécuter des tests d'intégration, consultez l'articleIntegration Testing with Maven.

Avec Jenkins, nous devons configurer le travail avec:

This build is parametrized

Et lesString parameter:test.mime=xml ajoutés. **

Une configuration Jenkins commune consisterait à exécuter des travaux exécutant le même ensemble de tests d’intégration sur le service déployé - l’un avec XML et l’autre avec des représentations JSON.

7. Conclusion

Cet article a montré comment tester une API REST fonctionnant avec plusieurs représentations. La plupart des API publient leurs ressources sous plusieurs représentations. Il est donc essentiel de toutes les tester. Le fait que nous puissions utiliser exactement les mêmes tests pour tous est tout simplement génial.

L'implémentation complète de ce mécanisme - en utilisant des tests d'intégration réels et en vérifiant les représentations XML et JSON - peut être trouvée dansthe GitHub project.