【问题标题】:Mock set return type based on object's properties and not its reference基于对象属性而不是其引用的模拟集返回类型
【发布时间】:2018-04-04 08:25:59
【问题描述】:

由于模拟框架 (NSubstitute) 的工作方式,我在单元测试中遇到了问题。

我想测试一个接收参数的方法,在该方法内部,我正在使用 new 运算符创建一个新对象,并将这个新对象传递给将构建另一个对象的构建器。我的问题是我不能模拟构建器来返回我想要的,因为当我配置返回对象时,它会根据引用来做。

所以如果我新创建的对象是这样的:

class MyReferenceType
{
    public String Property1 { get; set; }

    public String Property2 { get; set; }

    public String Property3 { get; set; }
}

如果我的模拟我会创建一个MyReferenceType 类型的新对象,我会这么说

myBuilder.Build(myReferenceTypeObject).Returns(anotherObject);

在我的方法中,对象myReferenceTypeObject 将有另一个引用,它不会返回我想要的对象。

那么有没有办法根据对象的属性内容而不是它的引用来配置模拟的返回对象?

这是一些代码:

class Mapper
{
    private Builder builder;
    public Mapper(Builder builder)
    {
        this.builder = builder;
    }

    public string Map(string data)
    {
        //process the string 

        MyReferenceType obj = new MyReferenceType();

        return this.builder.Build(obj);
    }
}

【问题讨论】:

  • 您能否提供一个我们可以测试的完整示例?这将使验证解决方案变得更加容易。
  • @DaisyShipton 给你
  • 什么是Builder?试图使用它的测试在哪里?理想情况下,我们应该能够复制、粘贴、编译和运行示例,而无需猜测其他任何内容。
  • 代码很大...但这是它的一个简单版本...我认为没有人将所有生产代码放在这里...您可以看到 builder.Build() 将返回MyReferenceType 类型的字符串
  • 我不是要你发布你的整个生产代码。我要求您发布一个完整的示例。是的,我可能会花更多时间猜测其余代码的样子,但是 a) 我可能猜错了,浪费了大家的时间; b) one 人(寻求帮助的人)花一点时间在问题中将一个完整的例子放在一起比 everyone 试图帮助你的人更合适不得不这样做。

标签: c# unit-testing mocking nsubstitute


【解决方案1】:

没有办法让引用匹配。在被测方法之外,您无法控制该对象,因为它正在方法中进行初始化。

使用Arg.Any<T>() 作为参数,以使模拟期望在执行时更加灵活,因为它将忽略传递的特定参数。

根据提供的代码示例,一个简单的测试可能如下所示

//Arrange
var data = "some data";
var myBuilder = Substitute.For<Builder>();
var expected = "some value";
myBuilder.Build(Arg.Any<MyReferenceType>()).Returns(expected);

var subject = new Mapper(myBuilder);

//Act
var actual = subject.Map(data);

//Assert
Assert.AreEqual(expected, actual);

这将允许模拟在调用时按预期运行。

如果您想有条件地匹配参数,请使用Arg.Is&lt;T&gt;(Predicate&lt;T&gt; condition)

myBuilder
  .Build(Arg.Is<MyReferenceType>(_ => _.Property1 == "value1" && _.Property2 == "value2"))
  .Returns(expected);

如果传递的参数满足预期条件,则行为应与上述预期相同。

参考NSubstitute: Argument matchers

【讨论】:

    【解决方案2】:

    这不是问题的答案,而是另一种测试方法,可以消除您面临的问题。

    而不是创建一个模拟使用 Builder 的实际实现 - 那么你的测试将很简单

    // Arrange
    var givenData = "some data";
    var expected = "transformed data";
    
    var builder = new Builder();
    var mapper = new Mapper(builder);
    
    // Act
    var actual = mapper.Map(data);
    
    // Assert
    actual.Should().Be(expected);
    

    使用Builder 的实际实现将使您有可能在Map 方法内进行更改并更改为Builder 而无需更改测试。

    仅模拟会使您的测试变慢的类/方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2017-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-12
      相关资源
      最近更新 更多