2016-10-04 1 views
0

J'ai une bibliothèque implémentant un UIControl personnalisé avec une méthode qui déclencherait un événement .valueChanged lorsqu'il est appelé. Je voudrais tester la méthode pour ce comportement.Comment tester si un UIControlEvents a été déclenché

Mon contrôle personnalisé:

class MyControl: UIControl { 
    func fire() { 
     sendActions(for: .valueChanged) 
    } 
} 

Et le test:

import XCTest 

class ControlEventObserver: NSObject { 
    var expectation: XCTestExpectation! 

    init(expectation anExpectation: XCTestExpectation) { 
     expectation = anExpectation 
    } 

    func observe() { 
     expectation.fulfill() 
    } 
} 

class Tests: XCTestCase { 
    func test() { 
     let myExpectation = expectation(description: "event fired") 
     let observer = ControlEventObserver(expectation: myExpectation) 
     let control = MyControl() 
     control.addTarget(observer, action: #selector(ControlEventObserver.observe), for: .valueChanged) 
     control.fire() 
     waitForExpectations(timeout: 1) { error in 
      XCTAssertNil(error) 
     } 
    } 
} 

Le problème est la méthode observe obtient jamais appelé si le expectation n'est pas remplie.

La question est: comment pouvons-nous tester pour UIControlEvents comme dans ce cas? Peut-être que nous devons forcer le runloop en quelque sorte?

EDIT 1: Veuillez noter que depuis que je teste une bibliothèque, ma cible de test ne possède aucune application hôte. Le test ci-dessus passe lorsque la cible de test a une application hôte.

+1

c'est bizarre J'ai copié et collé votre code dans un projet que j'ai créé et l'attente est complète, test réussi. – Wilson

+0

Je me suis rendu compte que c'était parce que je suis en train de tester une bibliothèque. Lorsque je crée une autre cible de test avec une application hôte, le test ci-dessus passe. @WilsonBalderrama –

Répondre

4

Apple's documentation for UIControl déclare que:

Lorsqu'un événement spécifique de contrôle se produit, le contrôle appelle toutes les méthodes d'action associés tout de suite. Les méthodes d'action sont distribuées via l'objet UIApplication actuel, qui trouve un objet approprié à gérer le message, en suivant la chaîne du répondeur si nécessaire.

Lorsque sendActions(for:) est appelé sur un UIControl, le contrôle appellera les UIApplication « s sendAction(_:to:from:for:) pour livrer l'événement à la cible enregistrée.

Étant donné que je teste une bibliothèque sans application hôte, il n'y a pas d'objet UIApplication. Par conséquent, l'événement .valueChanged n'est pas distribué et la méthode observe n'est pas appelée.

0

Vous déclarez l'objet observateur dans la méthode de test. Cela signifie que dès que la méthode est terminée, elle sera libérée de la mémoire et n'est donc pas appelée. Créez une référence à l'observateur au niveau de la classe dans la classe Tests comme suit et cela fonctionnera.

class Tests: XCTestCase { 

    var observer: ControlEventObserver! 
    func test() { 
     let myExpectation = expectation(description: "event fired") 
     self.observer = ControlEventObserver(expectation: myExpectation) 
     let control = MyControl() 
     control.addTarget(observer, action:#selector(ControlEventObserver.observe), for: .valueChanged) 
     control.fire() 
     waitForExpectations(timeout: 1) { error in 
      XCTAssertNil(error) 
     } 
    } 
} 

Vous aurez également besoin du myExpectation & control à déclarer de la même manière d'autre qui ne sera pas appelé non plus.

+0

Merci pour votre réponse. Je pense que les objets ne sont pas libérés car je ne crée aucun pool de publication automatique. De plus, je teste une bibliothèque de sorte que la cible de test ne possède aucune application hôte. J'ai révisé ma question pour refléter cela. –