我有2个班级:
SecondDeep.cs
我做了简单的代码,例如:
class FirstDeep
{
public FirstDeep() { }
public string AddA(string str)
{
SecondDeep sd = new SecondDeep();
bool flag = sd.SomethingToDo(str);
if (flag == true)
str = string.Concat(str, "AAA");
else
str = string.Concat(str, "BBB");
return str;
}
}
和
class SecondDeep
{
public bool SomethingToDo(string str)
{
bool flag = false;
if (str.Length < 10)
{
//todo something in DB, and after that flag should be TRUE
}
return flag;
}
}
然后我想为方法“AddA”编写单元测试:
class Tests
{
[Test]
public void AddATest()
{
string expected = "ABCAAA";
FirstDeep fd = new FirstDeep();
string res = fd.AddA("ABC");
Assert.AreEqual(expected, res);
}
}
之后我遇到了麻烦,我不知道我的Test类中的SomethingToDo方法的写入存根是否正确。我总是假的。我应该返回TRUE。但是如何?
答案 0 :(得分:11)
允许您编写存根的好方法是使用依赖注入。 FirstDeep
取决于SecondDeep
,在您的测试中,您希望将SecondDeep
替换为存根。
首先通过提取SecondDeep
的接口来更改现有代码,然后将其注入构造函数中的FirstDeep
:
interface ISecondDeep {
Boolean SomethingToDo(String str);
}
class SecondDeep : ISecondDeep { ... }
class FirstDeep {
readonly ISecondDeep secondDeep;
public FirstDeep(ISecondDeep secondDeep) {
this.secondDeep = secondDeep;
}
public String AddA(String str) {
var flag = this.secondDeep.SomethingToDo(str);
...
}
}
请注意,FirstDeep
不再创建SecondDeep
个实例。而是在构造函数中注入实例。
在测试中,您可以为ISecondDeep
创建存根,其中SomethingToDo
始终返回true:
class SecondDeepStub : ISecondDeep {
public Boolean SomethingToDo(String str) {
return true;
}
}
在测试中,您使用存根:
var firstDeep = new FirstDeep(new SecondDeepStub());
在生产代码中,您使用&#34; real&#34; SecondDeep
:
var firstDeep = new FirstDeep(new SecondDeep());
使用依赖注入容器和存根框架可以使这很容易。
如果您不想重写代码,可以使用框架拦截Microsoft Moles等来电。在Visual Studio的下一个版本中,Fakes Framework中将提供类似的技术。
答案 1 :(得分:4)
要使代码可测试,请不要在类中实例化依赖项。使用dependency injection(通过构造函数,属性或参数)。还可以使用抽象类或接口来模拟依赖项:
class FirstDeep
{
private ISecondDeep oa;
public FirstDeep(ISecondDeep oa)
{
this.oa = oa;
}
public string AddA(string str)
{
return String.Concat(str, oa.SomethingToDo(str) ? "AAA" : "BBB");
}
}
根据抽象,您可以单独测试您的课程。
interface ISecondDeep
{
bool SomethingToDo(string str);
}
class SecondDeep : ISecondDeep
{
public bool SomethingToDo(string str)
{
bool flag = false;
if (str.Length < 10)
{
// without abstraction your test will require database
}
return flag;
}
}
这是测试样本(使用Moq)。它向您展示了如何将true
从调用返回到您的模拟依赖项:
[TestFixture]
class Tests
{
[Test]
public void AddAAATest()
{
// Arrange
Mock<ISecondDeep> secondDeep = new Mock<ISecondDeep>();
secondDeep.Setup(x => x.SomethingToDo(It.IsAny<string>())).Returns(true);
// Act
FirstDeep fd = new FirstDeep(secondDeep.Object);
// Assert
Assert.That(fd.AddA("ABD"), Is.EqualTo("ABCAAA"));
}
}