【问题标题】:Moq in unit Testing单元测试中的起订量
【发布时间】:2012-08-21 21:19:27
【问题描述】:

假设我有一个服务

public interface IAreaService
{
   int CalculateArea(int x,int y);
   int CalculateAreaTimesX(int x, int y, int ammount);
}

public class AreaService : IAreaService
{
    public int CalculateArea(int x,int y)
    {
        return x*y;
    }

    public int CalculateAreaTimesX(int x, int y, int ammount)
    {
        return CalculateArea(x, y)*ammount;
    }
}

配合相关的单元测试

[TestMethod]
    public void AreaService_GetArea_Test()
    {
        AreaService service = new AreaService();
        int expected = 9;
        int actual  = service.CalculateArea(3, 3);
        Assert.AreEqual(expected,actual,"The Calculate area does not work as expected");
    }

    [TestMethod]
    public void AreaService_GetAreaMultiplyByX_TestTrueValue()
    {
        AreaService service = new AreaService();
        int expected = 27;
        int actual = service.CalculateAreaTimesX(3, 3, 3);
        Assert.AreEqual(expected,actual);
    }

好的,在运行单元测试之后。我确信我的方法是有效的,生活应该很棒。

但是现在我想在另一个类中使用 IAreaService,这就是我失去光的地方。这是另一个类的实现。

public class PriceCalculatorService
{
    private readonly IAreaService _areaService;
    public PriceCalculatorService(IAreaService areaService)
    {
        _areaService = areaService;
    }


    public double GetPrice(int x, int y, int times, double price)
    {
       return  _areaService.CalculateAreaTimesX(x, y, times)*price;
    }
}

如果我运行以下单元测试(我的想法可能在这里模拟错误,这就是问题所在。

[TestMethod]
    public void PriceCalculatorService_GetPrice_Test()
    {
        var IAreaServiceMock = new Mock<IAreaService>();
        IAreaServiceMock.Setup(ism => ism.CalculateAreaTimesX(2, 2, 2)).Returns(8);
        PriceCalculatorService priceCalc = new PriceCalculatorService(IAreaServiceMock.Object);

        double expected = 20;
        double actual = priceCalc.GetPrice(2, 2, 2, 2.50);
        Assert.AreEqual(expected,actual);
    }

问题

当我运行上面提到的所有单元测试时,一切都很好。他们都通过了。但是让我们说我出于某种原因需要将 AreaService.Calculate() 方法更改为以下

public int CalculateArea(int x,int y)
{
    return x*y+2;
}

这意味着我的单元测试“AreaService_GetArea_Test()”将失败,就像它应该的那样,但是由于“PriceCalculatorService_GetPrice_Test()”测试中使用的模拟仍然会通过,因为看起来当你模拟一个服务时,然后不使用实际代码(显然)。所以我的 PriceCalculatorService_GetPrice_Test 没用。但是我使用存根然后单元测试将失败,因为它应该。

那么什么时候模拟,什么时候不模拟?

【问题讨论】:

  • 但这就是测试units的想法。您正在孤立地测试单独的单元。你的AreaService_GetArea_Test 会失败,这才是最重要的——你会立即看到哪个单元不能正常工作。如果它导致 10 次级联失败并且您仍然必须调试整个代码以识别“是谁的错”,那会更好吗?它不会违背单元测试的目的吗? :)
  • 是的,光线正在缓慢但肯定地亮起。在您之前刚刚解决问题之后,以这种方式做事是一个非常大的思维转变。但我相信这是值得的。谢谢。

标签: unit-testing moq


【解决方案1】:

所以我的 PriceCalculatorService_GetPrice_Test 没用。

不,不是。它正在测试GetPrice 的代码,测试GetPrice 的代码。在我看来是合理的。

在该方法中没有太多要测试的内容,但您正在对其进行测试。如果您没有三次使用相同的参数会更好(例如,使用 1、2、3 会更好)但您正在测试它。

没有理由仅仅因为接口的实现发生变化而中断测试。您在AreaService_GetArea_Test() 中发现了对AreaService 的更改,这确实是您应该发现的地方。

【讨论】:

  • 我担心的是,当另一个开发人员更改方法时,他会看到测试失败,但因为在他看来,这是要走的路,他会将单元测试更改为适应他的变化。这意味着所有其他测试都会出错,因为它们在模拟这个测试。
  • @Captain0:这取决于更改的目的。如果它是违反接口契约的更改,那么实现就会被破坏。如果是界面的变化,那不是问题。
【解决方案2】:

PriceCalculatorService_GetPrice_Test 正在测试GetPrice。它正在测试GetPrice 的返回值是否为_areaService.CalculateAreaTimesX(x, y, times) 乘以price。这就是测试的意义。所以它根本没有用。

GetPrice 应该依赖于CalculateAreaTimesX 的结果,而不是该方法的逻辑。所以无论模拟返回什么,都可以进行测试。

【讨论】:

    猜你喜欢
    • 2017-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多