【问题标题】:Mocking and testing classes with multiple constructor parameters使用多个构造函数参数模拟和测试类
【发布时间】:2011-07-01 00:58:18
【问题描述】:

我的服务层类是为构造函数注入设置的。在模拟和测试这些类时,我如何避免必须为我不会在测试中使用的参数传入 null

我试图避免的示例代码:

[TestFixture]
public class SomeServicesTests
{
    SomeServices _someServices;

    [SetUp]
    public void Initialize()
    {
        _someServices= new SomeServices (null, 
                                        new StubRepositoryOne(), 
                                        null, 
                                        null, 
                                        new StubRepositoryTwo(), 
                                        null, 
                                        null, 
                                        null,
                                        null);

    }

    [Test]
    public void Test_Something()
    {
        string input = "yes";
        string exptectedOutput = "no";

        string output = _someServices.SomeFunction(input); // uses StubRepositoryOne and StubRepositoryTwo
        Assert.AreEqual(exptectedOutput, output);
    }
}

【问题讨论】:

    标签: unit-testing nunit mocking moq


    【解决方案1】:

    我通常验证注入参数与空值。我通常只是通过

    new Mock<T>().Object
    

    为了这些。

    【讨论】:

    • 但是如果StubRepositoryOneStubRepositoryTwoSomeServices 中都是私有的,我该如何传递?
    • @Omar - 我会为依赖项使用公共接口而不是私有类。存根...类实际上是存根吗?如果是这样并且您正在使用 Moq(正如您的标签所暗示的那样),那么让 Moq 即时创建您的界面存根(如我上面所示 - 用您的界面替换 T)。
    • 所以没有办法让 Moq 创建我的服务类,存根除我指定的参数之外的所有参数 StubRepositoryOne/StubRepositoryTwo?
    • @Omar - 您不希望 Moq 创建您的测试类。你想要一个实际的实例来测试,而不是一个模拟/假/存根。 Moq 创建虚假的依赖项。它是一个隔离/模拟框架,而不是 IoC 容器。我建议您阅读更多有关测试的内容,例如 The Art Of Unit Testing 一书。
    【解决方案2】:

    存根所有缺失的参数,并始终检查 SomeService 中的空参数。每次使用时检查输入是否为 null 很麻烦。因此,最好始终在构造函数中检查 null,并抛出 ArgumentNullException。

    您提到某些输入参数是私有的。必须将它们提取出来,您的注入模式才能正常工作。

    【讨论】:

      【解决方案3】:

      在我的答案已被接受后添加,但是...

      仅需要为测试传递一些依赖项这一事实表明可能存在设计问题。一个很好的试金石是,一个类型中的所有字段都应该在每个方法中使用——很难一直这样做——但如果你不能这样做,这意味着该类可能会被分解为具有更精细职责的更小的类.当你分解它们时,每个类将只采用它们立即需要的依赖项。

      另一方面,如果您在这里所做的是手动服务定位器,并且您只是测试功能的子集,您可能需要考虑创建仅测试构造函数。如:

      internal SomeServices(IServiceOne one, IServiceTwo two)
      {
      }
      

      或者使用 getter/setter 公开服务并进行相应的分配。同样,此处的 internal 关键字可用于保持您的设计意图干净:

      public IServiceOne One
      {
         get { return _one; }
         internal set { _one = value; }
      }
      

      当然,您希望将 InternalsVisibleTo 属性添加到您的代码中,以允许您的测试访问这些内部方法。

      【讨论】:

      • 你说得对,我确实闻到了服务类的设计问题。
      猜你喜欢
      • 2019-09-05
      • 1970-01-01
      • 2012-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-01
      • 2011-01-04
      • 2022-08-19
      相关资源
      最近更新 更多