2016-05-24 20 views
14

Sto provando a testare una classe ma sono un po 'confuso su cosa testare. Qui è la classe che voglio unit test:Swift - Unità test di variabili e metodi privati ​​

class CalculatorBrain { 

    private var accumulator = 0.0 

    func setOperand(operand: Double) { 
     accumulator = operand 
    } 

    var result: Double { 
     return accumulator 
    } 

    private var operations: Dictionary<String, Operation> = [ 
     "=" : .Equals, 

     "π" : .Constant(M_PI), 
     "e" : .Constant(M_E), 

     "±" : .UnaryOperation({ (op1: Double) -> Double in return -op1 }), 
     "√" : .UnaryOperation(sqrt), 
     "cos": .UnaryOperation(cos), 

     "+" : .BinaryOperation({ (op1: Double, op2: Double) -> Double in return op1 + op2 }), 
     "−" : .BinaryOperation({ (op1: Double, op2: Double) -> Double in return op1 - op2 }), 
     "×" : .BinaryOperation({ (op1: Double, op2: Double) -> Double in return op1 * op2 }), 
     "÷" : .BinaryOperation({ (op1: Double, op2: Double) -> Double in return op1/op2 }) 
    ] 

    private enum Operation { 
     case Constant(Double) 
     case UnaryOperation((Double) -> Double) 
     case BinaryOperation((Double, Double) -> Double) 
     case Equals 
    } 

    func performOperation(symbol: String) { 
     if let operation = operations[symbol] { 
      switch operation { 
      case .Constant(let value): 
       accumulator = value 
      case .UnaryOperation(let function): 
       accumulator = function(accumulator) 
      case .BinaryOperation(let function): 
       executePendingBinaryOperation() 
       pendingBinaryOperation = PendingBinaryOperationInfo(binaryOperation: function, firstOperand: accumulator) 
      case .Equals: 
       executePendingBinaryOperation() 
      } 
     } 
    } 

    private var pendingBinaryOperation: PendingBinaryOperationInfo? 

    private struct PendingBinaryOperationInfo { 
     var binaryOperation: (Double, Double) -> Double 
     var firstOperand: Double 
    } 

    private func executePendingBinaryOperation() { 
     if let pending = pendingBinaryOperation { 
      accumulator = pending.binaryOperation(pending.firstOperand, accumulator) 
      pendingBinaryOperation = nil 
     } 
    } 
} 

Per il codice di cui sopra, che cosa sarebbe buoni test.

Vale la pena testare ogni singola operazione (+, -, *, /, ecc.) Nel dizionario operations?

Vale la pena provare i metodi privati?

+1

Vedere http://programmers.stackexchange.com/questions/750/what-should-you-test-with-unit-tests – zneak

risposta

14

Il test dell'unità per definizione è il test della scatola nera, il che significa che non ti interessa l'interno dell'unità che collaudi. Siete principalmente interessati a vedere qual è l'output dell'unità in base agli input forniti nel test dell'unità.

Ora, dalle uscite possiamo affermare da diversi fattori:

  • il risultato di un metodo
  • lo stato dell'oggetto dopo che agisce su di essa,
  • l'interazione con le dipendenze dell'oggetto contiene

In tutti i casi, ci interessa solo l'interfaccia pubblica, poiché è quella che comunica con il resto del mondo.

Le cose private non hanno bisogno di test di unità semplicemente perché ogni oggetto privato è indirettamente utilizzato da un elemento pubblico. Il trucco è di scrivere abbastanza test che esercitano i membri pubblici in modo che quelli privati ​​siano completamente coperti.

Inoltre, una cosa importante da tenere a mente è che il test dell'unità dovrebbe convalidare le specifiche dell'unità e non la sua implementazione. La convalida dei dettagli di implementazione aggiunge un forte accoppiamento tra il codice di test unitario e il codice testato, il che ha un grande svantaggio: se i dettagli di implementazione testati cambiano, è probabile che anche il test dell'unità debba essere modificato, e questo diminuisce il vantaggio di avere unit test per quel pezzo di codice.

+1

Supponiamo ad esempio di testare solo il metodo pubblico '' 'performOperation''' per vedere se il comportamento è corretto. Questo sarebbe che non sto testando tutte le diverse operazioni di implementazione nel '' 'operations'''' '' Dictionary'''.Non sarebbe una cattiva idea dato che non sto testando la diversa implementazione dell'operazione. Non sarebbe una cattiva idea? – breaktop

+0

@breaktop: l'input fornito al metodo pubblico 'performOperation' può coprire tutte le varie operazioni. Se uno o più input non producono l'output atteso, sai quali implementazioni private vengono interrotte senza doverle testare esplicitamente. È ancora possibile scrivere il test su 'performOperation' pubblica e testare vari input, quindi regolare l'implementazione privata come richiesto in base ai risultati. – mc01

+1

@breaktop se si aggiungono abbastanza test per 'performOperation' per esercitare tutti i percorsi di codice nel dizionario' operazioni' privato, allora si avrà una copertura completa sul membro privato. – Cristik

12

Non è possibile testare i metodi privati ​​in Swift utilizzando @testable. È possibile provare solo i metodi contrassegnati con internal o public. Come dicono i documenti:

Nota: @testable consente l'accesso solo a funzioni "interne"; Le dichiarazioni "private" non sono visibili al di fuori del loro file anche quando utilizza @testable.

Leggi tutto here

0

ho trovato this link che sta dicendo qualcosa di simile con Cristik.

Fondamentalmente, stai facendo la domanda sbagliata, non dovresti cercare di testare la classe/le funzioni contrassegnate con "privato".