Comment puis-je tester mon service Web REST à l'unité?

16

Je suis nouveau dans les tests unitaires, j'ai une méthode Web REST qui appelle simplement DB et remplit un DTO. Le pseudo-code est

public object GetCustomer(int id)
{
  CustomerDTO objCust = //get from DB
  return objCust;
}

Mon doute est de savoir comment écrire des tests pour ces méthodes et le type de tests (intégration / unité) à inclure. Et pour les tests unitaires, faut-il frapper la base de données. Si c'était le cas et que je transmettais un identifiant client et que je fais peu d'assertions, les données pourraient éventuellement changer, ce qui entraînerait des échecs.

Je pense que je manque quelque chose ici pour comprendre ces concepts.

Ensoleillé
la source
5
Dans le code que vous avez publié, les choses à tester sont les suivantes: (1) Pouvez-vous appeler GetCustomer avec un int comme paramètre. (2) Retourne-t-il un objet CustomerDTO? (3) Cet objet est-il rempli à partir de la base de données comme prévu. (4) Un comportement attendu se produit-il s'il est appelé avec un int qui ne correspond pas à un client valide? Rien de tout cela n'a encore à voir avec REST. Lorsque vous êtes prêt à écrire le code qui répond aux demandes RESTful, vous allez écrire des tests pour celui-ci.
DavidO
@DavidO: "Cet objet est-il rempli à partir de la base de données comme prévu?" n'est décidément pas un test unitaire (en ce qui concerne le code OP). C'est un test d'intégration.
Flater
Oui tu as raison. Si je pouvais revenir en arrière et modifier le commentaire pour mentionner que dans un test d'intégration, vous vérifieriez le composant DB, et sinon vous vous moqueriez de lui, je ferais cette modification, mais la fenêtre d'édition pour les commentaires si 5 minutes, et le commentaire a été fait six il y a des années. :)
DavidO

Réponses:

18

Pendant les tests unitaires, vous ne devez pas tester avec une base de données, ou du moins, pas avec une base de données que vous n'avez pas préparée pour les tests unitaires. Le test avec une base de données et, en tant que tel, le test simultané de différentes couches de votre application est généralement considéré comme un test d'intégration . Avec les tests unitaires, vous êtes censé tester uniquement ce que fait votre méthode, ce qu'elle renvoie en fonction de différents paramètres et quand (ou non) elle doit échouer.

Il est très attendu que dans votre méthode, vous appeliez des méthodes X à partir d'autres classes. Vous ne testez pas ces méthodes X , vous devez donc vous moquer de ces méthodes.

Je suppose que vous écrivez votre code en Java, dans ce cas, vous disposez d'excellents cadres de simulation tels que Mockito qui peuvent vous être utiles. Que vous utilisiez ou non un cadre de simulation est votre choix, je dirai simplement qu'il vous fera gagner beaucoup de temps et celui que j'ai mentionné au moins n'est vraiment pas compliqué.

Si vous voulez simplement écrire votre propre maquette pour expérimenter, alors supposez que vous avez la CustomerRepositoryclasse suivante :

public class CustomerRepository {
 public CustomerDTO getCustomer(int id) {
   ...
 }
}

Vous pouvez écrire votre propre CustomerRepositoryclasse moquée et sale de la manière suivante:

public class MockedCustomerRepository extends CustomerRepository {
 public boolean bThrowDatabaseException;
 public boolean bReturnNull;
 public boolean bReturnCustomerWrongId;
 public boolean bReturnCustomerWithId;
 public CustomerDTO getCustomer(int id) {
  if(bThrowDatabaseException) { 
    throw new DatabaseException("xxx"); 
  } else if(bReturnNull) { 
    return null; 
  } else if(bReturnCustomerWrongId) { 
    throw new CustomerNotExistException(id);
  } else if(bReturnCustomerWithId) { 
    return new CustomerDTO(id); 
  }
 }
}

Ensuite, dans votre scénario de test, vous remplacez essentiellement votre instance "standard" de CustomerRepositorypar une instance simulée qui vous permettra de tester votre méthode pour différents résultats de getCustomer:

public class CustomerRestTest {
  public void testGetCustomer_databaseFailure() {
    MockedCustomerRepository dto = new MockedCustomerRepository();
    dto.bThrowDataBaseException = true;
    yRestClass rest = new MyRestClass();
    rest.dto = dto;
    rest.getCustomer(0);
    // depending on what you do in your getCustomer method, you should check if you catched the exception, or let it pass, etc.. Make your assertions here

  public void testGetCustomer_customerNotExist() {
    // etc.
  }
}

En règle générale, chaque méthode de test ne doit tester qu'une seule chose, ce qui permet de garder vos tests petits et concentrés sur une seule tâche.

Je vais le répéter :-) L'écriture d'une classe moquée entière prend un certain temps comme vous le voyez. Envisagez d'utiliser un cadre de simulation, moins on écrit de code, moins on fait d'erreurs , non? Se moquer d'une méthode qui lève une exception ou renvoie une valeur donnée pour un paramètre donné est un jeu d'enfant et prend 2 ou 3 lignes (avec mockito au moins)

J'espère que cela vous aidera à tester votre méthode REST.

Jalayn
la source
4
Habituellement, vous n'avez pas de logique dans vos classes DTO, en particulier aucune qui interagit avec votre stockage de données.
JustAnotherUserYouMayKnowOrNot
1
Ce n'était qu'un exemple, mais vous avez absolument raison. Je vais changer les exemples pour qu'ils correspondent mieux à la théorie.
Jalayn