2017-05-20 4 views
0

Puis-je simuler l'objet SKPhysicsContact pour alimenter la méthode -(void)didEndContact:(SKPhysicsContact *)contact? Ou y a-t-il d'autres techniques qui peuvent être exploitées ici?Existe-t-il un moyen d'écrire un test d'une fonction `SKPhysicsContactDelegate`?

class PhysicsTestCase: XCTestCase { 

    var physics: GamePhysics! 

    ... 

    func testCaseOfCollisionsHandling() { 

     let contact = SKPhysicsContact() 
     contact.bodyA = SKPhysicsBody(circleOfRadius: 10) // Error, 'bodyA' is get-only property 

     physics.didEnd(contact) // Physics conforms to `SKPhysicsContactDelegate` 
    } 

    ... 

} 

... 

// The class that is being tested 

class GamePhysics: NSObject, SKPhysicsContactDelegate { 

    // MARK: - SKPhysicsContactDelegate 

    func didBegin(_ contact: SKPhysicsContact) { 

     guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else { 
      fatalError("No nodes in colliding bodies") 
     } 

     switch (nodeB, nodeA) { 

     case let (ball as LogicalBall, tile as LogicalTile): 
      // Performing some logic 

     ... 

     } 
    } 

    func didEnd(_ contact: SKPhysicsContact) { 

     ... 
    } 

    ... 
} 
+0

Vous pouvez créer un petit projet et encore vos objets sur initialise dessus de l'autre. 'didEndContact' devrait être appelé dans cette situation. –

+0

Qu'essayez-vous exactement de faire? Pourquoi essayez-vous de définir le corps de la physique? Ce n'est pas quelque chose que tu ferais jamais. –

+0

Je veux écrire un ensemble de tests qui vérifie que mon 'SKPhysicsContactDelegate' fonctionne comme prévu. – Zapko

Répondre

0

Bien que, subclas chanter proposé par Jon Reid dans https://stackoverflow.com/a/44101485/482853 est très soigné, je n'ai pas réussi à le faire fonctionner dans ce cas particulier en raison de la nature insaisissable de SKPhysicsContact classe elle-même.

La façon dont ce problème peut être résolu est d'utiliser le bon vieux Objectif runtime C:

func testBallsCollisionIsPassedToHandler() { 

    let ballAMock = LogicalBallMock() 
    let bodyA = SKPhysicsBody(circleOfRadius: 10) 
    bodyA.perform(Selector(("setRepresentedObject:")), with: ballAMock) // So the bodyA.node will return ballAMock 

    let ballBMock = LogicalBallMock() 
    let bodyB = SKPhysicsBody(circleOfRadius: 10) 
    bodyB.perform(Selector(("setRepresentedObject:")), with: ballBMock) // So the bodyB.node will return ballBMock 

    let contact = SKPhysicsContact() 
    contact.perform(Selector(("setBodyA:")), with: bodyA) 
    contact.perform(Selector(("setBodyB:")), with: bodyB) 

    physics.didEnd(contact) 

    // Assertions ...  

} 
0

Lorsque nous ne pouvons pas changer un type parce que nous ne possédons pas l'API, la solution est d'utiliser la technique du code existant et Remplacer Méthode Sous:

class TestablePhysicsContact: SKPhysicsContact { 
    var stubbedBodyA: SKPhysicsBody? 

    override var bodyA: SKPhysicsBody { 
     return stubbedBodyA! 
    } 
} 

Pour utiliser dans votre exemple test:

func testCaseOfCollisionsHandling() { 
     let contact = TestablePhysicsContact() 
     contact.stubbedBodyA = SKPhysicsBody(circleOfRadius: 10) 

     physics.didEnd(contact) 

     // assert something 
    } 

Pour en savoir plus sur cette technique, voir http://qualitycoding.org/swift-partial-mock/

+0

Salut Jon, quel honneur d'avoir une réponse de votre part:) J'ai récemment tombé sur votre blog, et j'en ai eu de bonnes idées. Je vous remercie! – Zapko

+0

Malheureusement, la solution que vous avez proposée ne fonctionne pas. Les crashs de test parce que 'SKPhysicsContact' est une classe parapluie et' contact' s'avère être de type 'PKPhysicsContact' en cours d'exécution, même si nous l'initions en tant que' TestablePhysicsContact'. – Zapko

+0

Mais merci pour l'idée! C'est vraiment propre. Je suis tellement en train d'esquiver le sous-classement qui n'a même pas pensé à ce tour. – Zapko