it-swarm-ja.com

Swiftでサブクラス化してスタブを作成することのデメリットはありますか?

私は主にSwift言語に関する回答に興味がありますが、より一般的な回答はいつでも歓迎します。

クラス(CUT)をテストしたいので、そのために依存性注入を使用します。今、私はすることができます:

  1. CUTが使用するプロトコルを作成します。これは、いくつかの具象クラスといくつかのスタブクラスが準拠します。
  2. CUTで具象クラスを使用し、具象クラスのサブクラスとしてスタブクラスを使用するだけです。スタブクラスは、CUTが具象クラスから使用しているメソッドをオーバーライドしますjust

オプション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)
    }
}

オプション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)
    }
}

オプション2を使用することに欠点はありますか?

  • ABはより緊密に結合されていますか?
  • クラスBが別のフレームワークの一部である場合、問題はありますか?将来的には問題になるかもしれませんSwift ABからのメソッドを使用する場合、それはpublicであり、openではないため、オーバーライドできません(たとえば、value())?
1
Aleksa

Swiftはよりプロトコル指向の設計に入ります

私はOption 1に行きます

特別なクラスの代わりにプロトコルに準拠する場合、任意の種類(クラス、構造体、列挙型)のスタブを交換できます。コードはこの特定のクラスに結合されていません。

したがって、単純な構造体で複雑なクラスをモックすることができます。

3
muescha

潜在的な欠点の1つは、スタブがクリーンから始まっていないことです。エラーが発生しやすいすべてのメソッドを意図的にオーバーライドしない限り、サブクラス化されたスタブは基本クラスから動作を継承しますが、これは予期しない場合があります。

たとえば、Aが次のように実装されている場合:

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

    func doSomethingUnexpected() {
        //melt things
    }
}

次に、スタブされたBがBからサブクラス化されているため、スタブはデフォルトでDoSomethingUnexpected()の実装を継承しており、これはテストによって呼び出されます。スタブはもはや実際にはスタブではない可能性があるため、それは便利な機能かもしれませんし、テストが実行していることを少しぼんやりさせているかもしれません。

0
Weyland Yutani