web-dev-qa-db-fra.com

Se moquer de Swift

Comment vous moquez-vous d'un objet dans Swift?

Le protocole Mirror semblait prometteur, mais il ne fait pas grand chose pour le moment.

Jusqu'à présent, la seule approche que j'ai trouvée consiste à sous-classer et à remplacer toutes les méthodes de la classe fausse. Ce n'est bien sûr pas une vraie maquette, loin d'être idéale, et beaucoup de travail.

D'autres idées?

Pourquoi pas OCMock?

De la source :

Puis-je utiliser OCMock en utilisant la fonctionnalité de passerelle de langue? 

Oui mais avec des limitations. Si vous êtes courageux. À partir de maintenant, c'est hautement expérimental. Il n'y a aucune garantie que OCMock supportera jamais complètement Rapide.

Limites connues:

  • Les tests doivent être écrits en Objective-C
  • Les objets à simuler doivent hériter de NSObject
  • Pas de stubbing/attente/vérification des méthodes de classe
48
hpique

NSHipster touche aux fonctionnalités linguistiques de Swift qui rendent une bibliothèque moqueuse externe moins nécessaire:

Dans Swift, les classes peuvent être déclarées dans la définition d'une fonction, ce qui permet aux objets fictifs d'être extrêmement autonomes. Déclarez simplement une méthode de classe intérieure, une substitution et une méthode nécessaire:

func testFetchRequestWithMockedManagedObjectContext() {
    class MockNSManagedObjectContext: NSManagedObjectContext {
        override func executeFetchRequest(request: NSFetchRequest!, error: AutoreleasingUnsafePointer<NSError?>) -> [AnyObject]! {
            return [["name": "Johnny Appleseed", "email": "[email protected]"]]
        }
    }

    ...
}

La possibilité de créer une sous-classe de votre dépendance externe dans la portée locale, en plus de l’ajout de XCTestExpectation, résout de nombreux problèmes identiques à ceux de OCMock.

Une bibliothèque très utile, telle que OCMock, offre des méthodes très utiles pour vérifier que les classes factices ont bien été appelées. Il est possible de l'ajouter manuellement, mais l'ajout automatique est Nice.

25
Drew Beaupre

Je crée mes classes factices en intégrant tout dans un protocole. Je lance à la main un simulacre de classe pour se conformer au protocole en question, comme suit:

protocol Dog: class {
    var name: String { get }

    func bark()
}

class DogImpl: Dog {
    var name: String

    init(name: String) {
        self.name = name
    }

    func bark() {
        print("Bark!")
    }
}

class DogMock: Dog {
    var name = "Mock Dog"
    var didBark = false

    func bark() {
        didBark = true
    }
}

J'utilise cela conjointement avec l'injection de dépendance pour obtenir une couverture complète du test. C'est beaucoup de casse-tête, mais je n'ai eu aucun problème avec cette approche jusqu'à présent.

En ce qui concerne les sous-classes moqueuses, vous aurez des problèmes avec les classes final ou si elles ont des initialiseurs non triviaux.

4
Mark

Je veux signaler quelque chose en plus de la réponse marquée - je ne sais pas si c'est un bug ou non. 

Si vous sous-classez NSObject d'une manière ou d'une autre (dans mon cas, je sous-classais UIView qui sous-classe NSObject en interne), vous devez déclarer explicitement la fonction remplacée avec @objc sinon votre test ne sera pas compilé. Dans mon cas, le compilateur se bloque avec:

Erreur de segmentation: 11

Donc la classe suivante:

public class ClassA: UIView
{
    @objc public func johnAppleseed() {

    }
}

Devraient être testés à l'unité de la manière suivante:

class ClassATests: XCTestCase {

    func testExample()
    {
        class ClassAChildren: ClassA
        {
            @objc private override func johnAppleseed() {

            }
        }
    }
}
2
hris.to

Vous pouvez réaliser ce genre de moquerie avec MockFive

En résumé, vous devez créer une maquette "à la main" de la classe source que vous souhaitez simuler ...

mais ensuite, vous pouvez remplacer ses méthodes de façon dynamique, de sorte que vous pouvez l’utiliser où vous en avez besoin et configurer son comportement là-bas. 

J'ai écrit un article sur la façon de l'utiliser ici .

1
Daniel Burbank

Je recommande d'utiliser Coucou , qui peut gérer la plupart des tâches de moquage standard.

Exemple de cours:

class ExampleObject {

    var number: Int = 0

    func evaluate(number: Int) -> Bool {
        return self.number == number
    }

}

class ExampleChecker {

    func check(object: ExampleObject) -> Bool {
        return object.evaluate(5)
    }

}

Exemple de test:

@testable import App
import Cuckoo
import XCTest

class ExampleCheckerTests: XCTestCase {

    func testCheck() {
        // 1. Arrange
        let object = MockExampleObject().spy(on: ExampleObject())
        stub(object) { object in
            when(object.evaluate(any())).thenDoNothing()
        }
        let checker = ExampleChecker()

        // 2. Action
        checker.check(object)

        // 3. Assert
        _ = verify(object).number.get
        verify(object).evaluate(any())
        verifyNoMoreInteractions(object)
    }

}
0
sundance

En raison des limitations que vous avez écrites, OCMock ne fonctionne pas très bien dans Swift (chaque framework moqueur étant fortement dépendant du moteur d’exécution).

Il existe cependant plusieurs cadres moqueurs pour Swift, de la génération simulée semi-manuelle à la génération quasi entièrement automatique. Certains d'entre eux figurent déjà dans les réponses, je vais donc en recommander un autre, dont je suis l'un des auteurs.

https://github.com/MakeAWishFoundation/SwiftyMocky

Je n'entrerai pas beaucoup dans les détails, il a ses limites mineures, mais d'après ce que je vois, il possède le plus vaste ensemble de fonctionnalités des frameworks Swift (du moins ceux que je connais), y compris la prise en charge des génériques, les membres @objc et la mise à jour des simulacres. vous écrivez/modifiez des protocoles (mode observateur). 

0
Andrzej Michnia