Tutoriel: comment écrire des tests avec Vapor 2

Dans ce didacticiel, je vais vous montrer comment tester vos itinéraires en fonction du résultat du didacticiel CRUD que j'ai écrit précédemment. Si vous voulez d'abord faire le didacticiel CRUD et revenir plus tard, vous pouvez le trouver ici. Si vous voulez ignorer le didacticiel CRUD, vous pouvez cloner le résultat ici et commencer tout de suite!

Vous pouvez trouver le résultat de ce test sur github: ici.

1. Structure du projet

Vous devriez avoir une structure de projet comme celle-ci:

exemple de test /
├── Package.swift
├── Sources /
├── App /
│ ├── Config + Setup.swift
│ ├── Droplet + Setup.swift
│ ├── Routes.swift
│ └── Modèles /
│ └── User.swift
└── └── Run /
├── Tests /
└── AppTests /
├── UserRequestTest.swift
└── Utilitaires.swift
├── Config /
├── Public /
├── Dépendances /
└── Produits /

Si vous avez cloné le référentiel à partir du tutoriel crud, vous aurez déjà le fichier UserRequestTest.swift. Dans ce cas, supprimez tout le code qu'il contient.

Si vous avez créé le projet crud en suivant le tutoriel, vous aurez une structure de projet comme celle-ci:

exemple de test /
├── Package.swift
├── Sources /
├── App /
│ ├── Config + Setup.swift
│ ├── Droplet + Setup.swift
│ ├── Routes.swift
│ └── Modèles /
│ └── User.swift
└── └── Run /
├── Tests /
└── AppTests /
├── PostControllerTests.swift
├── RouteTests.swift
└── Utilitaires.swift
├── Config /
├── Public /
├── Dépendances /
└── Produits /

Dans ce cas, effacer
• PostControllerTests.swift
• RouteTests.swift
et créez un fichier vide nommé
• UserRequestTest.swift

exemple de test /
├── Package.swift
├── Sources /
├── App /
│ ├── Config + Setup.swift
│ ├── Droplet + Setup.swift
│ ├── Routes.swift
│ └── Modèles /
│ └── User.swift
└── └── Run /
├── Tests /
└── AppTests /
├── PostControllerTests.swift <- DELETE
│ ├── RouteTests.swift <- DELETE
├── UserRequestTest.swift <- CREATE
└── Utilitaires.swift
├── Config /
├── Public /
├── Dépendances /
└── Produits /

2. Test: l'utilisateur est créé

Dans Tests / AppTests / UserRequestTest.swift, écrivez:

importer Vapor
importer HTTP
importer XCTest
// import App est nécessaire pour enregistrer directement l'utilisateur comme "try User (nom d'utilisateur:" Hero ", âge: 23 ans) .save ()"
@testable import App
classe UserRequestTest: TestCase {
  // obtenir une instance de notre drop avec notre configuration
  laisser tomber = essayer! Droplet.testable ()
  func testThatUserGetsCreated () jette {
    /// MARK: PREPARATION
    let un = "Tim", age = 21
    laisser utilisateur = utilisateur (nom d'utilisateur: un, age: age)
  
    laisser json = essayer user.makeJSON ()
    laissez reqBody = try Body (json)
    /// MARK: TESTING
    let req = Request (méthode: .post, uri: "/ user", en-têtes: ["Content-Type": "application / json"], corps: reqBody)
    laisser res = essayer drop.testResponse (to: req)
    // la réponse est 200
    res.assertStatus (is: .ok)
    // réponse test est json
    gardien laisse resJson = res.json else {
      XCTFail ("Erreur lors de l'obtention de json de res: \ (res)")
      revenir
    }
    essayez res.assertJSON ("id", passe: {jsonVal dans jsonVal.int! = nil})
    essayez res.assertJSON ("nom d'utilisateur", est égal à: un)
    try res.assertJSON ("age", est égal à: age)
    /// MARK: CLEANUP
    guard let userId = resJson ["id"] ?. int, laissez userToDelete = try User.find (userId) else {
      XCTFail ("Erreur impossible de convertir id en int ou impossible de trouver l'utilisateur avec l'id de la réponse: \ (res)")
      revenir
    }
    essayez userToDelete.delete ()
  }
}

Laissez-moi vous expliquer brièvement (à part les commentaires) ce que fait le code ci-dessus. J'ai développé un modèle personnel pour structurer les fonctions de test:

func testThatUserGetsCreated () jette {
  /// MARK: PREPARATION
  Ceci marque le début de la zone où je prépare toutes les données dont j'ai besoin pour tester
  /// MARK: TESTING
  Cela marque le début de la zone où je teste réellement
  /// MARK: CLEANUP
  Ceci marque le début de la zone où je nettoie les données éventuellement créées après le test.
}

Nous voulons tester si notre route / utilisateur lorsqu'il est déclenché avec une requête POST-Request envoyant un fichier JSON valide crée réellement un utilisateur.

UNE FONCTION:
J’ai trouvé comment nommer la fonction après ce qu’ils ont fait de très utile et expressif, c’est pourquoi nous l’appelons testThatUserGetsCreated ().

EN TRAIN DE PRÉPARER:
Dans ce domaine, nous créons deux variables, un (nom d'utilisateur) et age (représente l'âge). Nous initialisons notre utilisateur avec ces variables et faisons de lui un objet JSON. Nous initialisons un corps avec l’objet JSON et c’est tout.

ESSAI:
Nous créons un objet de requête en passant par la méthode http, l’url, l’en-tête et le corps dont nous avons besoin pour la route. En activant, essayez drop.testResponse (to: req) notre requête est exécutée et nous sauvegardons le résultat dans la variable res.
Nous pouvons maintenant tester si la réponse contient ce que nous avons défini dans route à renvoyer après avoir créé avec succès un utilisateur.

Nous avons défini que la route renvoie l'utilisateur créé sous forme de code JSON.

Nous vérifions donc avec guard si la réponse contient JSON. Sinon, nous savons que quelque chose s'est mal passé, jetant ainsi un XCTFail. Ensuite, nous vérifions si la valeur JSON au niveau de la clé id est de type int. De même, si la valeur JSON du nom d'utilisateur de la clé est égale à notre variable un et si la valeur JSON de l'âge de la clé est égale à notre variable d'âge. Si tout réussit, génial, nettoyons!

NETTOYER:
Puisque l'utilisateur a été créé avec succès, nous aurons une entrée dans notre base de données. Je pense que les tests devraient laisser des endroits meilleurs qu'ils ne les ont trouvés
Nous convertissons donc la valeur JSON de la clé id en un int et essayons de trouver l'utilisateur avec l'ID. Si nous avons l’utilisateur, nous exécutons simplement delete () sur lui et nous avons terminé

3. Lancer notre premier test

Il y a plusieurs façons d'exécuter votre test et elles sont toutes cool. Dans ce tutoriel, nous utiliserons la boîte à outils vapeur. Exécutez dans votre terminal dans le répertoire de votre projet:

test de vapeur

Il vous montrera une balle fraîche qui va et vient en quelques secondes et ensuite votre test passera! Whoop Whoop!

Astuce: si vous voulez une sortie plus prolixe, allez-y et exécutez-le: test rapide

4. Test: l'utilisateur est lu

Cette fonction est plus petite et puisque vous savez ce que signifie ce qui est le code

importer Vapor
importer HTTP
importer XCTest
// import App est nécessaire pour enregistrer directement l'utilisateur comme "try User (nom d'utilisateur:" Hero ", âge: 23 ans) .save ()"
@testable import App
classe UserRequestTest: TestCase {
  // obtenir une instance de notre drop avec notre configuration
  laisser tomber = essayer! Droplet.testable ()
  func testThatUserGetsCreated () jette {
    ...
  }
  func testThatUserGetsReturned () jette {
    /// MARK: PREPARATION
    let un = "Elon", age = 31
    laisser utilisateur = utilisateur (nom d'utilisateur: un, age: age)
    // créer l'utilisateur manuellement puisque nous voulons tester pour l'obtenir
    essayez user.save ()
    guard let userId = user.id?.int else {
      XCTFail ("Erreur lors de la conversion de l'ID utilisateur en int")
      revenir
    }
    /// MARK: TESTING
    let req = Request (méthode: .get, uri: "/ user / \ (userId)")
    laisser res = essayer drop.testResponse (to: req)
    res.assertStatus (is: .ok)
    essayez res.assertJSON ("nom d'utilisateur", est égal à: un)
    /// MARK: CLEANUP
    essayez user.delete ()
  }
}

Vous pouvez à nouveau aller chercher:

test de vapeur

Ou ce que je préfère parce que vous verrez toutes les fonctions exécutées lors de l'exécution de vos tests:

test rapide

5. Test: l'utilisateur est mis à jour

Parler n'est pas cher. Montre moi le code.

importer Vapor
importer HTTP
importer XCTest
// import App est nécessaire pour enregistrer directement l'utilisateur comme "try User (nom d'utilisateur:" Hero ", âge: 23 ans) .save ()"
@testable import App
classe UserRequestTest: TestCase {
  // obtenir une instance de notre drop avec notre configuration
  laisser tomber = essayer! Droplet.testable ()
  func testThatUserGetsCreated () jette {
    ...
  }
  func testThatUserGetsReturned () jette {
    ...
  }
  func testThatUserGetsUpdated () jette {
    /// MARK: PREPARATION
    laisser un = "Steve", age = 37
    laisser utilisateur = utilisateur (nom d'utilisateur: un, age: age)
    essayez user.save ()
    /// MARK: TESTING
    guard let userId = user.id?.int else {
      XCTFail ("Erreur lors de la conversion de l'ID utilisateur en int")
      revenir
    }
    // changer les données
    let newUn = "Craig"
    user.username = newUn
    laisser json = essayer user.makeJSON ()
    laissez reqBody = try Body (json)
    // l'utilisateur test est mis à jour
    let updateUserReq = Request (méthode: .put, uri: "/ user / \ (userId)", en-têtes: ["Content-Type": "application / json"], corps: reqBody)
    let updateUserRes = essayez drop.testResponse (à: updateUserReq)
    updateUserRes.assertStatus (est: .ok)
    try updateUserRes.assertJSON ("nom d'utilisateur", est égal à: newUn)
    /// MARK: CLEANUP
    essayez user.delete ()
  }
}

Savez-vous quoi exécuter dans la ligne de commande ?

Astuce: test rapide ou test de vapeur

6. Test: l'utilisateur est supprimé

Je pense qu'aucun mot n'est nécessaire puisque je commente également mon code. Mais si vous pensez que des explications manquent, merci de me le faire savoir dans la section commentaires!

importer Vapor
importer HTTP
importer XCTest
// import App est nécessaire pour enregistrer directement l'utilisateur comme "try User (nom d'utilisateur:" Hero ", âge: 23 ans) .save ()"
@testable import App
classe UserRequestTest: TestCase {
  // obtenir une instance de notre drop avec notre configuration
  laisser tomber = essayer! Droplet.testable ()
  func testThatUserGetsCreated () jette {
    ...
  }
  func testThatUserGetsReturned () jette {
    ...
  }
  func testThatUserGetsUpdated () jette {
    ...
  }
  func testThatUserGetsDeleted () jette {
    /// MARK: PREPARATION
    let user = User (nom d'utilisateur: "Jony", âge: 23)
    essayez user.save ()
    guard let userId = user.id?.int else {
      XCTFail ("Erreur lors de la conversion de l'ID utilisateur en int")
      revenir
    }
    /// MARK: TESTING
    let req = Request (méthode: .delete, uri: "/ user / \ (userId)", en-têtes: ["Content-Type": "application / json"], body: Body ())
    laisser res = essayer drop.testResponse (to: req)
    res.assertStatus (is: .ok)
    essayez res.assertJSON ("type", égal à: "succès")
  }
}

Et maintenant, le coup final dans votre terminal!

test rapide

ou tu le sais:

test de vapeur

Félicitations! Vous avez implémenté avec succès des tests avec Vapor !!

Merci beaucoup pour la lecture! Si vous avez des suggestions ou des améliorations faites le moi savoir! J'aimerais avoir de vos nouvelles!

Twitter / Github / Instagram