Ci sono dei lati negativi nella creazione di uno stub per sottoclasse in Swift?

1

Sono principalmente interessato alle risposte in merito a una lingua Swift, ma le risposte più generali sono sempre ben accette.

Voglio testare una classe (CUT), e per questo userò l'iniezione di dipendenza. Ora, posso:

  1. Crea un protocollo che verrà utilizzato dal mio CUT, a cui saranno conformi alcune classi concrete e alcune classi di stub.
  2. Basta usare la classe concreta nel mio CUT e avere una classe stub come sottoclasse della classe concreta. La classe Stub sostituirà solo i metodi che CUT sta utilizzando dalla classe concreta.

Esempio per l'opzione 1:

// PRODUCTION CODE

class A {
   func incrementedValueOf(_ b: BProtocol) -> Int {
       return b.value + 1
   }
}

protocol BProtocol {
    func value() -> Int
}

class B: BProtocol {
    func value() -> Int { return calculateValueSomehow() }
    func calculateValueSomehow() -> Int { return ... }
}

// TEST CODE

class BStub: BProtocol {
    var valueToProvide: Int = 0
    func value() -> Int { return valueToProvide }
}

class ATests: XCTestCase {

    func testIncrementedValueOf() {
        let bStub = BStub()
        bStub.valueToProvide = 11
        let a = A()
        XCTAssertEqual(a.incrementedValueOf(bStub), bStub.valueToProvide + 1)
    }
}

Esempio per l'opzione 2:

// PRODUCTION CODE

class A {
   func incrementedValueOf(_ b: B) -> Int {
       return b.value + 1
   }
}

// Note: BProtocol is removed

class B {
    func value() -> Int { return calculateValueSomehow() }
    func calculateValueSomehow() -> Int { return ... }
}

// TEST CODE

class BStub: B {
    var valueToProvide: Int = 0
    override func value() -> Int { return valueToProvide }

    // Note: calculateValueSomehow is not overridden
}

class ATests: XCTestCase {

    func testIncrementedValueOf() {
        let bStub = BStub()
        bStub.valueToProvide = 11
        let a = A()
        XCTAssertEqual(a.incrementedValueOf(bStub), bStub.valueToProvide + 1)
    }
}

C'è un lato negativo nell'usare l'opzione 2?

  • A e B sono più strettamente accoppiati?
  • C'è qualche problema se la classe B è una parte di un diverso framework? Forse sarà un problema in futuro Swift se A utilizza un metodo da B che è public ma non open , quindi non sarà sovrascrivibile (ad esempio value() )?
posta Aleksa 17.03.2017 - 16:55
fonte

2 risposte

3

swift entra in un design più orientato al protocollo

vorrei andare per Option 1

quando si conforma un protocollo invece di una classe speciale è possibile scambiare lo stub in qualsiasi tipo di tipo (classe, struct, enum). e il codice non è abbinato a questa classe specifica.

così puoi prendere in giro una classe complicata con una semplice struttura.

    
risposta data 28.03.2017 - 23:05
fonte
0

Uno svantaggio potenziale è che il tuo stub non parte da pulito. A meno che tu non modifichi di proposito tutti i metodi, che sono soggetti a errori, il tuo stub sottoclassificato erediterà il comportamento dalla classe base, il che potrebbe essere inaspettato.

Ad esempio, se A è implementato in questo modo:

class A {
    func incrementedValueOf(b : B) -> Int {
        b.doSomethingUnexpected();
        return b.value() + 1;
    }

    func doSomethingUnexpected() {
        //melt things
    }
}

Quindi, poiché le sottoclassi B stubbed da B, lo stub per impostazione predefinita ha ereditato l'implementazione di DoSomethingUnexpected () e questo verrà chiamato dal test. Forse è un'abilità utile, o forse rende un po 'confuso ciò che i test stanno facendo, perché lo stub potrebbe non essere più uno stub.

    
risposta data 28.04.2017 - 11:51
fonte

Leggi altre domande sui tag