简体   繁体   English

使用Moq验证参考参数的值

[英]Verify value of reference parameter with Moq

I just switched to Moq and have run into a problem. 我刚刚切换到Moq并遇到了问题。 I'm testing a method that creates a new instance of a business object, sets the properties of the object from user input values and calls a method (SaveCustomerContact ) to save the new object. 我正在测试一个创建业务对象的新实例的方法,从用户输入值设置对象的属性,并调用方法(SaveCustomerContact)来保存新对象。 The business object is passed as a ref argument because it goes through a remoting layer. 业务对象作为ref参数传递,因为它通过远程处理层。 I need to test that the object being passed to SaveCustomerContact has all of its properties set as expected, but because it is instantiated as new in the controller method I can't seem to do so. 我需要测试传递给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 );
}

Here's the test: 这是测试:

[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();

}

This is just the most recent of many attempts to get ahold of that parameter. 这只是获得该参数的许多尝试中最近的一次。 For reference, the value of actual does not change from its initial (constructed) state. 作为参考,实际值不会从其初始(构造)状态改变。

Moving the Assert.AreEqual(expected, actual) after the target call fails. 在目标调用失败后移动Assert.AreEqual(expected,actual)。 If I add .Verifiable() to the setup instead of the .CallBack and then call remote.Verify after the target (or, I assume, set the mock to strict) it always fails because the parameter I provide in the test is not the same instance as the one that is created in the controller method. 如果我将.Verifiable()添加到设置而不是.CallBack然后在目标之后调用remote.Verify(或者,我假设,将mock设置为strict)它总是失败,因为我在测试中提供的参数不是与在控制器方法中创建的实例相同的实例。

I'm using Moq 3.0.308.2. 我正在使用Moq 3.0.308.2。 Any ideas on how to test this would be appreciated. 关于如何测试这个的任何想法将不胜感激。 Thanks! 谢谢!

I can't offer you an exact solution, but an alternative would be to hide the pass-by-ref semantics behind an adapter, which takes the parameter by value and forwards it to the RemotingHandler. 我无法为您提供精确的解决方案,但另一种方法是隐藏适配器后面的pass-by-ref语义,该适配器按值获取参数并将其转发给RemotingHandler。 This would be easier to mock, and would remove the "ref" wart from the interface (I am always suspicious of ref parameters :-) ) 这将更容易模拟,并将从界面中删除“ref”疣(我总是怀疑ref参数:-))

EDIT: 编辑:

Or you could use a stub instead of a mock, for example: 或者您可以使用存根而不是模拟,例如:

public class StubRemotingHandler : IRemotingHandler
{
    public CustomerContact savedContact;

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

You can now examine the saved object in your test: 您现在可以检查测试中保存的对象:

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

You also say in your comment: 你还在评论中说:

I'd hate to start a precedent of wrapping random bits of the backend so I can write tests more easily 我不想开始包装后端的随机位的先例,所以我可以更容易地编写测试

I think that's exactly the precedent you need to set! 我认为这正是你需要设定的先例! If your code isn't testable, you're going to keep struggling to test it. 如果您的代码不可测试,那么您将继续努力测试它。 Make it easier to test, and increase your coverage. 使测试更容易,并增加您的覆盖范围。

The latest version of Moq supports this scenario. 最新版本的Moq支持此方案。

Taken from the quickstart at http://code.google.com/p/moq/wiki/QuickStart : 摘自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);

Unfortunately, I am not sure that this is possible without direct support from Moq. 不幸的是,如果没有Moq的直接支持,我不确定这是否可行。 The problem is that Lambda expressions do not support ref or out. 问题是Lambda表达式不支持ref或out。

"A lambda expression cannot directly capture a ref or out parameter from an enclosing method. " “lambda表达式不能直接从封闭方法中捕获ref或out参数。”

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

I can't even get an example like yours to work. 我甚至无法得到像你这样的例子来工作。 Adding ref to the setup fails to compile. 添加ref到设置无法编译。

You might want to check out the Moq discussions for more http://groups.google.com/group/moqdisc 您可以查看Moq讨论了解更多 http://groups.google.com/group/moqdisc

Good luck. 祝好运。

I have encountered similar issue. 我遇到过类似的问题。 Bit I got the solution by using the latest Moq and passing the value like 我通过使用最新的Moq并传递值来获得解决方案

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

Earlier as well, I was using the same method, but i was not getting return as true. 早些时候,我使用相同的方法,但我没有得到真正的回报。

Inside the actual function instance was getting created and overwriting the instance passed from the unit test class was causing the issue. 在实际的函数实例内部被创建并覆盖从单元测试类传递的实例导致了问题。 I removed instance creation inside the actual class and then it worked. 我删除了实际类中的实例创建,然后它工作了。

Hope it will help you. 希望它会对你有所帮助。

thanks 谢谢

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM