使用Moq验证参考参数的值

时间:2009-04-07 16:50:26

标签: c# mocking tdd moq-3

我刚刚切换到Moq并遇到了问题。我正在测试一个创建业务对象的新实例的方法,从用户输入值设置对象的属性,并调用方法(SaveCustomerContact)来保存新对象。业务对象作为ref参数传递,因为它通过远程处理层。我需要测试传递给SaveCustomerContact的对象是否按预期设置了所有属性,但因为它在控制器方法中被实例化为new,所以我似乎无法这样做。

public void AddContact() {

    var contact = new CustomerContact() { CustomerId = m_model.CustomerId };

    contact.Name = m_model.CustomerContactName;
    contact.PhoneNumber = m_model.PhoneNumber;
    contact.FaxNumber = m_model.FaxNumber;
    contact.Email = m_model.Email;
    contact.ReceiveInvoiceFlag = m_model.ReceiveInvoiceFlag;
    contact.ReceiveStatementFlag = m_model.ReceiveStatementFlag;
    contact.ReceiveContractFlag = m_model.ReceiveContractFlag;
    contact.EmailFlag = m_model.EmailFlag;
    contact.FaxFlag = m_model.FaxFlag;
    contact.PostalMailFlag = m_model.PostalMailFlag;
    contact.CustomerLocationId = m_model.CustomerLocationId;

    RemotingHandler.SaveCustomerContact( ref contact );
}

以下是测试:

[TestMethod()]
public void AddContactTest() {

    int customerId = 0;

    string name = "a";

    var actual = new CustomerContact();

    var expected = new CustomerContact() {
        CustomerId = customerId,
        Name = name
    };

    model.Setup( m => m.CustomerId ).Returns( customerId );
    model.SetupProperty( m => model.CustomerContactName, name );
    model.SetupProperty( m => m.PhoneNumber, string.Empty );
    model.SetupProperty( m => m.FaxNumber, string.Empty );
    model.SetupProperty( m => m.Email, string.Empty );
    model.SetupProperty( m => m.ReceiveInvoiceFlag, false );
    model.SetupProperty( m => m.ReceiveStatementFlag, false );
    model.SetupProperty( m => m.ReceiveContractFlag, false );
    model.SetupProperty( m => m.EmailFlag, false );
    model.SetupProperty( m => m.FaxFlag, false );
    model.SetupProperty( m => m.PostalMailFlag, false );
    model.SetupProperty( m => m.CustomerLocationId, 0 );

    remote
        .Setup( r => r.SaveCustomerContact( ref actual ) )
        .Callback( () => Assert.AreEqual( actual, expected ) );

    target.AddContact();

}

这只是许多尝试获得该参数的最新尝试。作为参考,实际值不会从其初始(构造)状态改变。

在目标调用失败后移动Assert.AreEqual(expected,actual)。如果我将.Verifiable()添加到设置而不是.CallBack然后在目标之后调用remote.Verify(或者,我假设,将mock设置为strict)它总是失败,因为我在测试中提供的参数不是与在控制器方法中创建的实例相同的实例。

我正在使用Moq 3.0.308.2。关于如何测试这个的任何想法将不胜感激。谢谢!

4 个答案:

答案 0 :(得分:19)

我无法为您提供精确的解决方案,但另一种方法是隐藏适配器后面的pass-by-ref语义,该适配器按值获取参数并将其转发给RemotingHandler。这将更容易模拟,并将从界面中删除“ref”疣(我总是怀疑ref参数:-))

编辑:

或者您可以使用存根而不是模拟,例如:

public class StubRemotingHandler : IRemotingHandler
{
    public CustomerContact savedContact;

    public void SaveCustomerContact(ref CustomerContact contact)
    {
        savedContact = contact;
    }
}

您现在可以检查测试中保存的对象:

IRemotingHandler remote = new StubRemotingHandler();
...
//pass the stub to your object-under-test
...
target.AddContact();
Assert.AreEqual(expected, remote.savedContact);

您还在评论中说:

  

我不想开始包装后端的随机位的先例,这样我就可以更轻松地编写测试了

我认为完全是您需要设置的先例!如果您的代码不可测试,那么您将继续努力测试它。使测试更容易,并提高您的覆盖率。

答案 1 :(得分:10)

最新版本的Moq支持这种情况。

取自http://code.google.com/p/moq/wiki/QuickStart的快速入门:

// ref arguments
var instance = new Bar();
// Only matches if the ref argument to the invocation is the same instance
mock.Setup(foo => foo.Submit(ref instance)).Returns(true);

答案 2 :(得分:9)

不幸的是,如果没有Moq的直接支持,我不确定这是否可行。问题是Lambda表达式不支持ref或out。

  

“lambda表达式不能直接从封闭方法中捕获ref或out参数。”

http://msdn.microsoft.com/en-us/library/bb397687.aspx

我甚至无法得到像你这样的例子来工作。将ref添加到设置无法编译。

您可能需要查看Moq讨论以获取更多信息 http://groups.google.com/group/moqdisc

祝你好运。

答案 3 :(得分:0)

我遇到过类似的问题。我通过使用最新的Moq并传递像

这样的值来获得解决方案

var instance = new Bar(); Mock.Setup(foo => foo.Submit(ref instance))。返回(true);

早些时候,我使用相同的方法,但我没有得到返回为真。

实际的函数实例内部被创建并覆盖从单元测试类传递的实例导致了问题。我删除了实际类中的实例创建,然后它工作了。

希望它会对你有所帮助。

感谢