我是编码的新手。
我有一个班级A
:
public class A
{
public InterfaceB _b;
public A()
{
_b = new B();
}
public string functionA()
{
if(String.IsNullOrEmpty(_b.GetId()))
return String.Empty;
else if(String.IsNullOrEmpty(_b.GetKey()))
return String.Empty;
else
return _b.GetToken();
}
}
public interface InterfaceB
{
string GetId();
string GetKey();
string GetToken();
}
我想测试functionA
我可以渗透到interfaceB
的所有三种方法。在我的单元测试中,我创建了一个类A
的实例,当我调用它时,我无法设置类B
的行为。
它一直在击中数据库,但我需要它用于其他测试用例。
我如何完全模仿这个以便我可以测试整个逻辑?
答案 0 :(得分:4)
为了能够测试A
和模拟界面InterfaceB
,您必须编写A
,以便它不负责创建InterfaceB
的实例。相反,它通过其构造函数接收InterfaceB
的实例。
你会一遍又一遍地看到这种模式:
public A()
{
private readonly InterfaceB _b;
public A(InterfaceB b)
{
_b = b;
}
public string functionA()
{
if(String.IsNullOrEmpty(_b.GetId())) return String.Empty;
else if(String.IsNullOrEmpty(_b.GetKey())) return String.Empty;
else return _b.GetToken();
}
}
这称为依赖注入。这意味着一个阶级的依赖是"注入"进入它,而不是创建它的那个类。当我们像这样注入构造函数时,我们也称它为#34;构造函数注入",但通常它只是依赖注入。"能够模仿你所询问的界面是我们使用它的原因之一。
一些关键细节:
InterfaceB
被传递给构造函数,A
中的任何内容都没有"知道"实际的实施是什么。它可能是任何东西。因此,A
永远不会与任何具体实现相关联。 (这就是为什么你可以"模拟" InterfaceB
。)_b
为readonly
。这不是绝对必要的,但这意味着_b
只能从构造函数中设置,并且永远不会再次更改。这强调A
只有接收并使用它。这个类永远不会控制_b
是什么。无论创建 A
确定该值是什么。现在,当您编写单元测试时,您可以创建完全符合您要求的InterfaceB
的模拟实现,例如
public class MockedInterfaceB : InterfaceB
{
private string _id;
private string _key;
private string _token;
public MockedInterfaceB(string id, string key, string token);
{
_id = id;
_key = key;
_token = token;
}
public string GetId() {return _id};
public string GetKey() {return _key};
public string GetToken() {return _token};
}
然后在您的单元测试中,您可以使用该实现:
var testA = new A(new MockedInterfaceB("myid","mykey","mytoken"));
您还可以使用Moq等工具更轻松地创建这些模拟。
当您听到依赖注入时,它通常位于依赖注入容器(如Castle Windsor,Autofac或Unity)的上下文中。这些是帮助您进行依赖注入工作的有用工具。他们值得学习。但依赖注入实际上只是关于你如何编写类,就像上面的例子中我们注入"依赖项(InterfaceB
)加入类A
。
答案 1 :(得分:0)
实际上,使用Typemock可以在不更改源代码的情况下测试您的方法。 在创建A的实例之前,您将能够将B模拟为未来实例,然后您可以修改B的方法行为。
例如:
[TestMethod,Isolated]
public void AllBMethodReturnStrings_WillReturnSuccess()
{
// Arrange
// Mocking future B's instance
var fakeIB = Isolate.Fake.NextInstance<B>();
var realA = new A();
Isolate.WhenCalled(()=> fakeIB.GetId()).WillReturn("fakeID");
Isolate.WhenCalled(() => fakeIB.GetKey()).WillReturn("fakeKey");
Isolate.WhenCalled(() => fakeIB.GetToken()).WillReturn("success");
// Act
var result = realA.functionA();
// Assert
Assert.AreEqual("success", result);
}