1

私は単体テスト、TDD、および一般的なモックを初めて使用しますが、一般的な考え方は理解しています。私の質問は、クラスをモックして、単体テストと実装クラスでコードを重複させずにインスタンス化されたメソッドを呼び出すことができるようにするにはどうすればよいですか? 以下を考えると:

//Simple Interface
public interface IProduct {
    double price { get; set; }
    double tax { get; set; }
    double calculateCost();
}

//Simple Implementation of IProduct
public class SimpleProduct : IProduct {
    private double _price;
    private double _tax;

    public double price {
        get { return _price; }
        set { _price = value; }
    }

    public double tax {
        get { return _tax; }
        set { _tax = value; }
    }

    public double calculateCost() {
        return _price + (_price * _tax);
    }
}
//Complex implementation of IProduct
public class MarylandProduct : IProduct {
    private double _price;
    private double _tax;

    public double price {
        get { return _price; }
        set { _price = value; }
    }

    public double tax {
        get { return _tax; }
        set { _tax = value; }
    }

    public double calculateCost() {
        if (_price <= 100) return _price + (_price * _tax);
        else {
            double returnValue = 100 + (100 * _tax); //use tax rate for first 100
            returnValue += (_price - 100) + ((_price - 100) * 0.05); //use a flat rate of 0.05 for everything over 100
            return returnValue;
        }
    }
}

次のような単体テストの作成を開始しました。

[TestMethod]
[HostType("Moles")]
public void molesCalculateCostforMarylandProduct() {
    //Assign
    MMarylandProduct marylandProduct = new MMarylandProduct();
    marylandProduct.priceGet = () => 1000;
    marylandProduct.taxGet = () => 0.07;

    const double EXPECTED = 1052;

    //Act
    double actual = marylandProduct.Instance.calculateCost();

    //Assert
    Assert.AreEqual(EXPECTED, actual);
}

単体テストでaMarylandProductまたは aのいずれかに対して calculate cost メソッドを呼び出せるようにしたいと考えています。SimpleProduct通常、価格と税金はデータベースから取得されますが、代わりに、データベースやサービス、またはこれらの値を提供する他のものとの結合を避けるために、これらの値がスタブ化されるようにしました。calculateCost()要するに、ユニットテストでそのメソッドをスタブすることなく機能をテストするユニットテストを書きたいということです.2年後にはロジックMarylandProductが変わることがわかっているからです。

したがって、たとえば、このテストを実行したら、コードを変更して、MarylandProduct.calculateCost()たとえば 750 を超える任意の価格に 50 を追加する「贅沢税」を追加できるはずです。これを行うと、単体テストが期待される値が 1052 であり、期待されMarylandProductているものとは異なる値を返すため、失敗します。

私はこれについて間違った方法で進んでいますか?TDDの精神が欠けているだけですか?助けてくれてありがとう。

編集:(私が試した他のモッキングフレームワークを追加)

[TestMethod]
    public void rhinoMockCalculateCostForMarylandProduct() {
        //assign
        IProduct marylandProduct = MockRepository.GenerateMock<IProduct>();
        marylandProduct.Stub(price => price.price).Return(1000);
        marylandProduct.Stub(tax => tax.tax).Return(0.07);

        const double EXPECTED = 1052;

        //act
        double actual = marylandProduct.calculateCost();

        //assert
        Assert.AreEqual(EXPECTED, actual);
    }

    [TestMethod]
    public void moqCalculateCostForMarylandProduct() {
        //assign
        var marylandProduct = new Mock<IProduct>();
        marylandProduct.Setup(price => price.price).Returns(1000);
        marylandProduct.Setup(tax => tax.tax).Returns(0.07);

        const double EXPECTED = 1052;

        //act
        double actual = ((MarylandProduct)marylandProduct.Object).calculateCost();

        //assert
        Assert.AreEqual(EXPECTED, actual);
    }

単体テストとクラスの実装に重複したコードを入れないようにしたいのは、クラスのコードが変更された場合でも、単体テストは変更されていないため合格するためです。単体テストでその変更を行うことが期待されていることは知っていますが、大量の単体テストがある場合、この種の設計を行うことは許容されますか? 単体テストと実装でコードが重複していますか?

4

1 に答える 1

4

さて、あなたが誤解しているように見えるのは、モックの目的です。モックは、テスト対象システム(SUT)を分離するために使用されます。したがって、SUTの依存関係をモックして、SUTを個別にテストできるようにします(たとえば、dbまたは一部のサービスへの依存関係を削除します)。テストしているオブジェクトをモックしません。そうでない場合は、何をテストしますか?

私があなたの質問を正しく理解している場合、あなたのドメインモデルにはIProductの実装があります(なぜ製品の異なる実装が必要なのかは別の質問です)。

その実装のCalculateCostメソッドをテストしたいとします。これにモックを使用する必要がある理由はわかりません。表面的には、Productに依存関係があってはならないため、モックするものはありません。

例えば

ドメインにこの製品があるとします。

public class MyProduct : IProduct {
    private double _price;
    private double _tax;

    public double Price {
        get { return _price; }
        set { _price = value; }
    }

    public double Tax {
        get { return _tax; }
        set { _tax = value; }
    }

    public double CalculateCost() {
        return //some complex logic here.
    }
}

このオブジェクトはすでに分離されており、依存関係がないため、モックする必要はありません。

それをテストするには、それを直接使用します。

[TestMethod]
public void CalculateCost() {
    //arrange
    var myProduct = new MyProduct();
    myProduct.Price = 1000;
    myProduct.Tax= 0.07;

    //act
    double actualCost = myProduct.CalculateCost();

    //assert
    double expectedCost = 1052;

    Assert.AreEqual(expectedCost, actualCost );
}

推奨読書: http: //www.manning.com/osherove/

于 2012-06-13T00:49:41.310 に答える