Pal*_*ain 5 c# nunit unit-testing moq
我有以下实现,
public interface IMath {
double Add(double a, double b);
double Subtract(double a, double b);
double Divide(double a, double b);
double Multiply(double a, double b);
double Factorial(int a);
}
public class CMath: IMath {
public double Add(double a, double b) {
return a + b;
}
public double Subtract(double a, double b) {
return a - b;
}
public double Multiply(double a, double b) {
return a * b;
}
public double Divide(double a, double b) {
if (b == 0)
throw new DivideByZeroException();
return a / b;
}
public double Factorial(int a) {
double factorial = 1.0;
for (int i = 1; i <= a; i++)
factorial = Multiply(factorial, i);
return factorial;
}
}
Run Code Online (Sandbox Code Playgroud)
我怎样才能测试Multiply()被称为ň时候ň的阶乘是如何计算的?
我正在使用NUnit 3和Moq.以下是我已经写过的测试,
[TestFixture]
public class CMathTests {
CMath mathObj;
[SetUp]
public void Setup() {
mathObj = new CMath();
}
[Test]
public void Add_Numbers9and5_Expected14() {
Assert.AreEqual(14, mathObj.Add(9, 5));
}
[Test]
public void Subtract_5From9_Expected4() {
Assert.AreEqual(4, mathObj.Subtract(9, 5));
}
[Test]
public void Multiply_5by9_Expected45() {
Assert.AreEqual(45, mathObj.Multiply(5, 9));
}
[Test]
public void When80isDividedby16_ResultIs5() {
Assert.AreEqual(5, mathObj.Divide(80, 16));
}
[Test]
public void When5isDividedBy0_ExceptionIsThrown() {
Assert.That(() => mathObj.Divide(1, 0),
Throws.Exception.TypeOf<DivideByZeroException>());
}
[Test]
public void Factorial_Of4_ShouldReturn24() {
Assert.That(mathObj.Factorial(4), Is.EqualTo(24));
}
[Test]
public void Factorial_Of4_CallsMultiply4Times() {
}
}
Run Code Online (Sandbox Code Playgroud)
我对使用Moq相当新,所以我现在还不太了解它.
你需要分离模拟部分和测试部分,因为Moq是关于消除依赖关系,而你的CMath类没有它们!
但基本上你不需要测试,Multiply被调用4次 - 它是内部实现.检测结果 :)
创建Separate Factorial类,以便乘法将在单独的界面中.
public interface IMath {
double Add(double a, double b);
double Subtract(double a, double b);
double Divide(double a, double b);
double Multiply(double a, double b);
}
public interface IFactorial {
double Factorial(int a, IMath math);
}
Run Code Online (Sandbox Code Playgroud)
在您的测试中,您可以创建模拟Math
[Test]
public void Factorial_Of4_CallsMultiply4Times()
{
var mathMock = new Mock<IMath>();
var factorial = new Factorial();
factorial.Factorial(4, mathMock.Object);
mathMock.Verify(x => x.Multiply(It.IsAny<double>()), Times.Exactly(4));
}
Run Code Online (Sandbox Code Playgroud)
public double Factorial(int a, Func<double,double,double> multiply = null)
{
multiply = multiply ?? CMath.Multiply;
double factorial = 1.0;
for (int i = 1; i <= a; i++)
factorial = multiply(factorial, i);
return factorial;
}
[Test]
public void Factorial_Of4_CallsMultiply4Times()
{
var mathMock = new Mock<IMath>();
var math = new CMath();
math.Factorial(4, mathMock.Object.Multiply);
mathMock.Verify(x => x.Multiply(It.IsAny<double>()), Times.Exactly(4));
}
Run Code Online (Sandbox Code Playgroud)
正如@aershov 已经说过的那样,您不需要测试是否Multiply已被调用 4 次。这是一个实现细节,您已经在 test 中进行了一定程度的测试Factorial_Of4_ShouldReturn24。您可能需要考虑使用该TestCase属性来为测试提供一系列输入,而不是单个值:
[TestCase(4, 24)]
[TestCase(2, 2)]
[TestCase(1, 1)]
[TestCase(0, 1)]
public void Factorial_OfInput_ShouldReturnExpected(int input, int expectedResult)
{
Assert.That(mathObj.Factorial(input), Is.EqualTo(expectedResult));
}
Run Code Online (Sandbox Code Playgroud)
@aershov 涵盖了两个设计更改,可以让您模拟您所询问的交互。第三个可以说是影响最小的更改是使您的Multiply方法virtual。这将允许您使用部分模拟来验证交互。更改如下所示:
执行
public class CMath : IMath
{
public virtual double Multiply(double a, double b)
{
return a * b;
}
// ...
Run Code Online (Sandbox Code Playgroud)
测试
Mock<CMath> mockedObj;
CMath mathObj;
[SetUp]
public void Setup()
{
mockedObj = new Mock<CMath>();
mockedObj.CallBase = true;
mathObj = mockedObj.Object;
}
[Test]
public void Factorial_Of4_CallsMultiply4Times()
{
mathObj.Factorial(4);
mockedObj.Verify(x => x.Multiply(It.IsAny<double>(),
It.IsAny<double>()), Times.Exactly(4));
}
Run Code Online (Sandbox Code Playgroud)
我不喜欢嘲笑被测系统(这通常是你做错了什么的好兆头),但是它确实允许你做你所要求的。
模拟可能非常有用,但是当您使用它们时,您需要仔细考虑您实际尝试测试的是什么。查看上面的测试,可以满足以下代码:
public double Factorial(int a)
{
double factorial = 1.0;
for (int i = 1; i <= a; i++)
factorial = Multiply(factorial, a);
return factorial;
}
Run Code Online (Sandbox Code Playgroud)
这段代码有一个严重的错误,它将源参数传递给循环的每次迭代,而不是循环计数器。其结果是非常不同的,但调用的数量是一样的,所以测试还通过。这是一个很好的迹象,表明测试实际上并没有增加价值。
事实上,测试实际上会增加摩擦,因为更难改变Factorial函数的实现。考虑 4! 的示例。所需的计算是4*3*2*1,但是最后一步乘以 1 本质上是一个 NOP,因为n*1=n。考虑到这一点,可以将阶乘方法稍微优化为:
public double Factorial(int a)
{
double factorial = 1.0;
for (int i = 2; i <= a; i++)
factorial = Multiply(factorial, i);
return factorial;
}
Run Code Online (Sandbox Code Playgroud)
factorial 方法的输入/输出测试将继续工作,但是 Mocked 测试计算Multiply中断的调用次数,因为计算答案只需要 3 次调用。
在决定使用模拟对象时,请始终考虑收益和成本。
| 归档时间: |
|
| 查看次数: |
1804 次 |
| 最近记录: |