使用Microsoft Moles模拟一个简单的类

时间:2012-06-12 13:51:55

标签: c# unit-testing moq rhino-mocks moles

我不熟悉单元测试和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);
}

我希望能够在单元测试中为MarylandProductSimpleProduct调用计算成本方法。通常获得价格和税收来自数据库,但我已经做了这样,以便这些值被存根以避免与数据库或服务或提供这些值的任何其他任何耦合。它归结为我想编写一个单元测试来测试calculateCost()的功能而不必在单元测试中存根该方法,因为我知道在{2}中的逻辑在{{1} } 将改变。

因此,例如,一旦我运行此测试,我应该能够进入并更改MarylandProduct的代码,以添加“奢侈税”,即在750以上的任何价格上加50.如果我这样做,我知道我的单元测试会失败,因为期望值是1052,现在MarylandProduct.calculateCost()正在返回除预期之外的其他内容。

我只是以错误的方式解决这个问题吗?我只是错过了TDD的精神吗? 谢谢你的帮助。

编辑:(添加我尝试过的其他模拟框架)

MarylandProduct

我想避免在单元测试和类的实现中放入重复的代码,因为如果类中的代码发生更改,那么单元测试仍将通过,因为它尚未更改。我知道预计会在单元测试中进行更改,但是如果你有单位测试的TON,那么这种设计是否可以接受?您的单元测试和实施中有重复的代码吗?

1 个答案:

答案 0 :(得分:4)

好的,你似乎误解的是模拟的目的。模拟用于隔离被测系统(SUT)。因此,您模拟SUT的依赖项,以便您可以单独测试SUT(例如,删除对db或某些服务的依赖性)。你没有嘲笑你正在测试的对象,否则,你在测试什么?

如果我正确理解了您的问题,那么您的域名模型会实现IProduct(为什么您需要不同的产品推广是另一个问题)。

您想测试这些实现的CalculateCost方法。我没有理由为什么你需要使用模拟器。从表面上看,Product不应该有任何依赖关系,因此没有什么可以模拟的。

e.g。

假设您在自己的域中拥有此产品:

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/