NSubstitute - 检查传递给方法的参数

25

我们目前正在从RhinoMocks迁移到NSubstitute。

我有一个接受DatabaseParams类型对象的方法。该类的结构如下(简化):

public class DatabaseParams
  {
    public string StoredProcName { get; private set; }
    public SqlParameter[] Parameters { get; private set; }

    public DatabaseParams(string storeProcName, SqlParameter[] spParams)
    {
      StoredProcName = storeProcName;
      Parameters = spParams;
    }
  }

我有以下方法,我想检查传递给它的参数是否正确:

public interface IHelper
{
Task<object> ExecuteScalarProcedureAsync(DatabaseParams data);
}

如何测试传递给该方法的DatabaseParams实例是否具有正确的值?

RhinoMocks中,我可以使用类似以下代码:

helperMock.Expect(m => m.ExecuteScalarProcedureAsync(Arg<DatabaseHelperParameters>.Matches(
        p =>   p.StoredProcName == "up_Do_Something"
            && p.Parameters[0].ParameterName == "Param1"
            && p.Parameters[0].Value.ToString() == "Param1Value"
            && p.Parameters[1].ParameterName == "Param2"
            && p.Parameters[1].Value.ToString() == "Param2Value"
        ))).Return(Task.FromResult<DataSet>(null));

helperMock 模拟了接口 IHelper,其中包含 ExecuteScalarProcedureAsync 方法。

3个回答

27

我已经自己找到了答案。

NSubstitute 只需要使用 .Received() 方法并指定方法参数。你可以将参数匹配规则作为一个谓词来指定。

例如:

  helperMock.Received().ExecuteScalarProcedureAsync(Arg.Is<DatabaseParams>(
   p =>   p.StoredProcName == "up_Do_Something"
        && p.Parameters[0].ParameterName == "Param1"
        && p.Parameters[0].Value.ToString() == "Param1Value"
        && p.Parameters[1].ParameterName == "Param2"
        && p.Parameters[1].Value.ToString() == "Param2Value"));

对于我的使用情况,我必须使用ReceivedWithAnyArgs而不是Received来调用我的对象。 - youngrrrr

24
有两种方法可以让您针对特定属性进行断言,从而更好地了解参数对象的哪些属性是不正确的。

使用Do

另一种方法是使用Do(请参阅https://nsubstitute.github.io/help/actions-with-arguments/)。例如:

StoredProc sp = null; // Guessing the type here

// Setup Do to capture arg
helperMock.ExecuteScalarProcedureAsync(Arg.Do<DatabaseParams>(p => sp = p));

// Call method
helperMock.ExecuteScalarProcedureAsync(dbParams);

// NUnit assertions, but replace with whatever you want.
Assert.AreEqual("up_Do_Something", sp.StoredProcName);
Assert.AreEqual("Param1", p.Parameters[0].ParameterName);
Assert.AreEqual("Param1Value", p.Parameters[0].Value.ToString());
Assert.AreEqual("Param2", p.Parameters[1].ParameterName);
Assert.AreEqual("Param2Value", p.Parameters[1].Value.ToString());

接收到的电话

ReceivedCalls 方法也可以使用,避免在调用被测试方法之前需要调用 Arg.Do

// Call method
helperMock.ExecuteScalarProcedureAsync(dbParams);

var sp = helperMock.ReceivedCalls().ToList().GetArguments()[0];

// NUnit assertions, but replace with whatever you want.
Assert.AreEqual("up_Do_Something", sp.StoredProcName);
Assert.AreEqual("Param1", p.Parameters[0].ParameterName);
Assert.AreEqual("Param1Value", p.Parameters[0].Value.ToString());
Assert.AreEqual("Param2", p.Parameters[1].ParameterName);
Assert.AreEqual("Param2Value", p.Parameters[1].Value.ToString());
ReceivedCalls 方法在官方文档中没有列出,但在上述情况下确实有效。

6
非常好的解决方案!但是对我来说,按照这种方式设置时并没有起作用。我必须先设置helperMock.ExecuteScalarProcedureAsync(Arg.Do<DatabaseParams>(p => sp = p));(也就是不带Received()这一部分),然后在稍后调用ExecuteScalarProcedureAsync()时将会设置sp - Tobias
1
感谢您的反馈。也许API已经发生了变化。我离开.NET世界有一段时间了,所以很高兴得到一些澄清。 - Castrohenge
1
请注意,在测试之前,您必须安排捕获变量。当您进行断言时,它将存在。 - Jess

4
有点晚了,但我也遇到了同样的需求。 我正在使用Java中的Mockito,并且喜欢他们的Argument capture helper。 它基本上与@Castrohenge的答案相同。
所以这是我的NSubstitute实现。
public interface IFoo
{
    void DoSomthing(string stringArg);
}

参数捕获类
public class ArgCapture<T>
{
    private List<T> m_arguments = new List<T>();

    public T capture()
    {
        T res = Arg.Is<T>(obj => add(obj)); // or use Arg.Compat.Is<T>(obj => add(obj)); for C#6 and lower
        return res;
    }

    public int Count
    {
        get { return m_arguments.Count; }
    }

    public T this[int index]
    {
        get { return m_arguments[index]; }
    }

    public List<T> Values {
        get { return new List<T>(m_arguments);}
    }

    private bool add(T obj)
    {
        m_arguments.Add(obj);
        return true;
    }
}

和使用测试案例

    [Test]
    public void ArgCaptureTest()
    {
        IFoo foo1 = Substitute.For<IFoo>();
        ArgCapture<string> stringArgCapture = new ArgCapture<string>();
        foo1.DoSomthing("firstCall");
        foo1.DoSomthing("secondCall");
        foo1.Received(2).DoSomthing(stringArgCapture.capture());
        Assert.AreEqual(2,stringArgCapture.Count);
        Assert.AreEqual("firstCall",stringArgCapture[0]);
        Assert.AreEqual("secondCall", stringArgCapture[1]);
    }

这个解决方案非常有效,能够生成整洁灵活的代码。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接