Unit testing 单元测试中的最小质量
让我们假设我有一个服务Unit testing 单元测试中的最小质量,unit-testing,moq,Unit Testing,Moq,让我们假设我有一个服务 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 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\u GetArea\u test()”将失败,就像它应该失败一样,但由于“PriceCalculatorService\u GetPrice\u test()”测试中使用的模拟仍然会通过,因为当你模拟一个服务时,似乎没有使用实际的代码(显然)
PriceCalculatorService\u GetPrice\u测试是无用的。但是我使用了一个存根,那么单元测试就会失败,因为它应该失败
那么什么时候嘲笑,什么时候不嘲笑
所以我的价格计算器服务价格测试是无用的
不,不是。它正在测试GetPrice
的代码,只有GetPrice
的代码对我来说是合理的
这种方法没有太多需要测试的地方,但你正在测试它。如果你没有三次使用相同的参数(例如,使用1、2、3会更好),那会更好,但你正在测试它
没有理由仅仅因为接口的一个实现发生了更改而导致该测试中断。您在AreaService\u GetArea\u test()
中捕捉到了对AreaService
的更改,这确实是您应该捕捉到的
所以我的价格计算器服务价格测试是无用的
不,不是。它正在测试GetPrice
的代码,只有GetPrice
的代码对我来说是合理的
这种方法没有太多需要测试的地方,但你正在测试它。如果你没有三次使用相同的参数(例如,使用1、2、3会更好),那会更好,但你正在测试它
没有理由仅仅因为接口的一个实现发生了更改就中断该测试。您在
AreaService\u GetArea\u test()中捕获到了对AreaService
的更改
,这确实是你应该抓住它的地方。PriceCalculatorService\u GetPrice\u Test
正在测试GetPrice
。它正在测试GetPrice
的返回值是否是\u区域服务。CalculateAreaTimesX(x,y,times)
乘以价格
。这就是测试的意义。因此它一点也不无用
GetPrice
应该取决于CalculateAreaTimesX
的结果,而不是该方法的逻辑。因此,无论模拟返回什么,都可以进行测试。PriceCalculatorService\u GetPrice\u test
正在测试GetPrice
。测试GetPrice
的返回值是否为.CalculateAreaTimesX(x,y,times)
乘以price
。这就是测试的意义。因此它一点都没有用处
GetPrice
应该取决于CalculateAreaTimesX
的结果,而不是该方法的逻辑。因此,无论模拟返回什么,测试都可以。但这就是测试单元的想法。您正在单独测试单独的单元。您的AreaService\u GetArea\u测试将失败,这才是最重要的-您会立即看到哪个单元工作不正常。如果它导致了10个级联失败,而您仍然需要调试整个代码以确定“谁的错”?它不会破坏单元测试的目的吗?:)是的,灯是缓慢但肯定亮起的。这样做是一个非常大的心态转变,在你之前刚刚对问题进行编码之后。但我相信这是值得的。谢谢。但这就是测试单元的想法。您正在单独测试单独的单元。您的AreaService\u GetArea\u测试将失败,这就是问题所在-您将立即看到哪个单元工作不正常。如果它导致了10次级联失败,而您仍然需要调试整个代码以确定“谁的错”,那会更好吗?它会不会挫败单元测试的目的呢?:)诚然,这盏灯正在缓慢但肯定地亮着。这样做是一个非常大的心态转变,在你之前刚刚对问题进行编码之后。但我相信这是值得的。谢谢。我担心的是,当另一个开发人员更改方法时,他会看到测试失败,但因为在他看来,这是一条路,他会更改单元测试以适应他的更改。这意味着所有其他的测试都是错误的,因为他们在模仿这个测试。@Captain0:这取决于更改的目的。如果这是一个违背接口契约的变更,那么实现就被破坏了。如果是接口内的更改,这不是问题。我关心的是,当另一个开发人员更改方法时,他会看到测试失败,但因为在他看来,这是一种方式,他会更改单元测试以适应他的更改。这意味着所有其他的测试都是错误的,因为他们在模仿这个测试。@Captain0:这取决于更改的目的。如果这是一个违背接口契约的变更,那么实现就被破坏了。如果这是接口内的更改,那么这不是问题。
public int CalculateArea(int x,int y)
{
return x*y+2;
}