要点
- 传参=复杂类或嵌套类时,必须通过接口,简化代码提高单测速度
- 传参=基础类型时,可通过虚拟成员,避免接口膨胀
- 依赖项提供2种功能(状态|交互),单测中区分处理可简化用例或提高覆盖率
- 适合读者:初步掌握接口|单测的开发者,欲知接口|单测所以然的开发者
- 自习关键字:控制反转(IOC),依赖注入(DI),可测试代码,模拟工具Moq,票根(stub)还是模拟(mock)
using System.Diagnostics;
namespace UnitTestWithInterface
{
// [类的单测]
// - 首先必须单测自身(ex.类A)并自证OK,然后在调用类(ex.类B)中作为依赖项
// - 作依赖项时,类A的结果值实现2种功能(状态|交互)
// - 状态:无关类B的运行流程,单测类B时Mock(模拟)类A的1种值即可,简化类B的单测组合
// - 交互:影响流程,单测类B时模拟类A的所有值(ex. true|false)
// 普通传参例@单测
// [ex.]
// Mock<FlatArgument> mock = new Mock<FlatArgument>(6);
// mock.Setup(x=>x.Trim("a+b+c+d")).Returns("a+b...");
public class FlatArgument
{
private readonly int maxLength;
// 传参基础类型,模拟成本低,避免接口膨胀
// 常见情形: ex. DateTime,静态基础类型
public FlatArgument(int maxLength = 6)
{
this.maxLength = maxLength;
}
// virtual: Mock用
public virtual string Trim(string fx)
{
// ... 实现逻辑(略)
return "a+b..."; // ex. fx = a+b+c+d
}
}
// 接口传参例@单测
// [example]
// Mock<IClassA> mock = new Mock<IClassA>();
// mock.Setup(x=>x.Validate()).Returns(true);
public class InterfaceAsArgument
{
private readonly IClassA a;
// 接口优势
// - 单测传参时无需生成复杂类或嵌套类,直接返回模拟结果
public InterfaceAsArgument(IClassA a)
{
Debug.Assert(a != null);
this.a = a;
}
public void Display()
{
if (a.Validate())
{
// 合法时细节(略)
return;
}
// 非法时细节(略);
}
}
public interface IClassA
{
bool Validate();
}
public class ClassA : IClassA
{
private readonly ClassB b;
// ClassB=嵌套类作参数
public ClassA(ClassB b)
{
Debug.Assert(b != null);
this.b = b;
}
public bool Validate()
{
// ... 实现逻辑(略)
return true;
}
}
public class ClassB { }
}
网友评论