单元测试c#mock接口由另一个接口组成

时间:2016-07-25 00:00:54

标签: c# .net unit-testing xunit.net

我是编码的新手。

我有一个班级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的行为。

它一直在击中数据库,但我需要它用于其他测试用例。

我如何完全模仿这个以便我可以测试整个逻辑?

2 个答案:

答案 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。)
  • 字段_breadonly。这不是绝对必要的,但这意味着_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);
}