【问题标题】:Verify value of reference parameter with Moq用 Moq 验证参考参数的值
【发布时间】:2010-10-18 02:50:21
【问题描述】:

我刚刚切换到起订量并遇到了问题。我正在测试一种方法,该方法创建业务对象的新实例,根据用户输入值设置对象的属性并调用方法 (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 );
}

这是测试:

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

}

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

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

我使用的是最小起订量 3.0.308.2。任何关于如何测试它的想法将不胜感激。谢谢!

【问题讨论】:

    标签: c# mocking tdd moq-3


    【解决方案1】:

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

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

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

    我什至找不到像你这样的例子。添加 ref 到 setup 编译失败。

    您可能想查看起订量讨论以了解更多信息 http://groups.google.com/group/moqdisc

    祝你好运。

    【讨论】:

      【解决方案2】:

      我无法为您提供准确的解决方案,但另一种方法是将传递引用语义隐藏在适配器后面,该适配器按值获取参数并将其转发给 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);
      

      你还在评论中说:

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

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

      【讨论】:

      • 我想我也不确定你会如何存根(尽管我对存根知之甚少)。您能否详细说明如何处理?
      • 另外,尽管我在 ref 问题上同意你的观点,但这就是我们的远程处理层的工作方式,我不想开始包装后端随机位的先例,这样我就可以编写测试更容易。
      • 我也遇到了类似的问题,并且非常专注于让 MoQ 工作,我忘记了我可以编写自己的存根!谢谢。
      【解决方案3】:

      最新版本的起订量支持此方案。

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

      【讨论】:

      • 感谢您的回复。我在快速入门中看到了这一点(事实上,我说服我的老板升级到最新版本的起订量,因为我希望它能正常工作)。不幸的是,该行为似乎不遵循“仅在调用的 ref 参数是同一实例时才匹配”所建议的(非常不清楚)。
      【解决方案4】:

      我也遇到过类似的问题。位我通过使用最新的最小起订量并传递类似的值得到了解决方案

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

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

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

      希望对你有所帮助。

      谢谢

      【讨论】:

        猜你喜欢
        • 2019-02-26
        • 2011-07-11
        • 2011-06-24
        • 2012-11-09
        • 2013-04-06
        • 2022-06-10
        • 1970-01-01
        • 2011-03-08
        • 1970-01-01
        相关资源
        最近更新 更多