C# 使用 Moq 验证参考参数的值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/726630/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:42:17  来源:igfitidea点击:

Verify value of reference parameter with Moq

c#mockingtddmoq-3

提问by Simon Calvin

I just switched to Moq and have run into a problem. 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. The business object is passed as a ref argument because it goes through a remoting layer. 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.

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

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.

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

Moving the Assert.AreEqual(expected, actual) after the target call fails. 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.

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

I'm using Moq 3.0.308.2. Any ideas on how to test this would be appreciated. Thanks!

我正在使用最小起订量 3.0.308.2。关于如何测试这一点的任何想法将不胜感激。谢谢!

采纳答案by Jim Arnold

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. This would be easier to mock, and would remove the "ref" wart from the interface (I am always suspicious of ref parameters :-) )

我无法为您提供确切的解决方案,但另一种方法是将传递引用语义隐藏在适配器后面,它按值获取参数并将其转发给 RemotingHandler。这将更容易模拟,并且会从界面中删除“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 exactlythe 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.

我认为这正是您需要树立的先例!如果您的代码不可测试,您将继续努力测试它。使测试更容易,并增加您的覆盖范围。

回答by Brian Genisio

Unfortunately, I am not sure that this is possible without direct support from Moq. The problem is that Lambda expressions do not support ref or out.

不幸的是,如果没有 Moq 的直接支持,我不确定这是否可行。问题是 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 morehttp://groups.google.com/group/moqdisc

您可能想查看 Moq 讨论以了解更多http://groups.google.com/group/moqdisc

Good luck.

祝你好运。

回答by Praveen Angyan

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

回答by superboy

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(); Mock.Setup(foo => foo.Submit(ref instance)).Returns(true);

var 实例 = new Bar(); Mock.Setup(foo => foo.Submit(ref instance)).Returns(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

谢谢