【问题标题】:How write stub method with NUnit in C#如何在 C# 中使用 NUnit 编写存根方法
【发布时间】:2012-05-16 18:02:36
【问题描述】:

我有 2 节课:

  • FirstDeep.cs
  • 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);
        }
    }

在那之后我遇到了麻烦,我不知道在我的测试类中为方法 SomethingToDo 编写存根有多正确。我总是假的。我应该返回 TRUE。但是怎么做呢?

【问题讨论】:

  • 您仍然可以使用您的模式:介绍bool expected = false; SecondDeep sd = new SecondDeep(); bool actualResult = sd.SomethingToDo("ABC"); Assert.AreEqual(excpected, actualResult); ...!?如果这不能满足您的需求,您可能会考虑详细说明和改进您的问题!
  • 你调试过你的代码吗?如果它发生在数据库中,我们无法为您提供帮助,因为我们没有关于那里发生的事情的详细信息。
  • 是的,我调试了我的代码,并在其中写道:“//在 DB 中做一些事情,之后该标志应该为 TRUE”我使用 .NET 中的类 MembershipUser 并且此方法无法连接到 DB,这就是为什么我应该在这种情况下返回 true。

标签: c# testing nunit stub


【解决方案1】:

允许您编写存根的一个好方法是使用依赖注入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());

在生产代码中,您使用“真实”SecondDeep

var firstDeep = new FirstDeep(new SecondDeep());

使用依赖注入容器和存根框架可以让很多事情变得更容易。

如果您不想重写代码,可以使用框架来拦截调用,例如 Microsoft Moles。在下一版本的 Visual Studio 中,Fakes Framework 将提供类似的技术。

【讨论】:

    【解决方案2】:

    为了使您的代码可测试,请不要在您的类中实例化依赖项。使用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"));
         }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-25
      相关资源
      最近更新 更多