【问题标题】:Moq: getting values back from ref parameters in test起订量:从测试中的参考参数中取回值
【发布时间】:2016-06-20 16:32:16
【问题描述】:

给定这个函数参考

void ParseFirstTwoRows(FileQueue file, ref string[] firstRow, ref string[] secondRow);

如何使用 Moq 构建依赖于 firstRow 和 secondRow 具有值的测试?

我试过了:

Mock<IFileParser> mockFileParse = new Mock<IFileParser>();

string[] firstline = new string[2] { "1", "2" };
string[] secondline = null;

mockFileParse.Setup(m => m.ParseFirstTwoRows(It.IsAny<FileQueue>(), ref firstline, ref secondline))
                .Callback<FileQueue, string[], string[]>((fq, fl, sl) =>
                {
                    fq = new FileQueue();
                    fl = firstline;
                    sl = secondline;
                });

return mockFileParse;

但这会导致错误:

无效的回调。使用参数设置方法 (FileQueue,String[]&,String[]&) 无法调用带参数的回调 (文件队列,字符串[],字符串[])。

我已经看到了几个关于此的问题,但我很困惑我正在尝试做的事情是否是不可能的,或者我是否只是没有正确地构建测试。我的测试只需要确保 ref 参数在执行代码中具有值。

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    您无需回电。只需确保在调用 SUT 时使用相同的 ref 参数,否则调用将失败。

    使用您的示例代码作为参考查看以下示例

    [TestClass]
    public class FileParserTest : MiscMoqTests {
        [TestMethod]
        public void Moq_With_Ref_Arguents() {
            //Arrange
            Mock<IFileParser> mockFileParse = new Mock<IFileParser>();
            // ref arguments
            string[] firstline = new string[2] { "1", "2" };
            string[] secondline = null;
            // Only matches if the ref arguments to the invocation are the same instance
            mockFileParse.Setup(m => m.ParseFirstTwoRows(It.IsAny<FileQueue>(), ref firstline, ref secondline)).Verifiable();
    
            var sut = new MyDummyClass(mockFileParse.Object);
    
            //Act
            sut.DoSomethingWithRef(ref firstline, ref secondline);
    
            //Assert
            mockFileParse.Verify();
        }
    
        public class MyDummyClass {
            private IFileParser fileParser;
    
            public MyDummyClass(IFileParser fileParser) {
                this.fileParser = fileParser;
            }
    
            public void DoSomethingWithRef(ref string[] firstRow, ref string[] secondRow) {
                var fq = new FileQueue();
                fileParser.ParseFirstTwoRows(fq, ref firstRow, ref secondRow);
    
            }
        }
    
        public interface IFileParser {
            void ParseFirstTwoRows(FileQueue file, ref string[] firstRow, ref string[] secondRow);
        }
        public class FileQueue { }
    }
    

    但是,如果您无法为 ref 提供相同的实例,那么看起来您将无法成功通过模拟。查看Quick Start for Moq 以了解它的功能。

    【讨论】:

    • 谢谢你,但是与 ref 的调用是从公共调用的私有函数,所以我不能像那样传递参数。这就是我的问题。
    • 在这种情况下,我建议您为该测试手动创建自己的模拟,并为该接口放弃 Moq。
    【解决方案2】:

    我通过重构解决了这个问题。虽然 Moq 在这种情况下不提供对 ref 参数的支持,但它确实适用于 out 参数。

    这是重构后的函数签名:

    void ParseFirstTwoRows(FileQueue file, out string[] firstRow, out string[] secondRow);
    

    这是测试设置:

    string[] first = { "'one'", "'two'", "'three'" };
    string[] second = { "'one'", "'two'", "'three'" };
    
    Mock<IFileParser> mockFileParse = new Mock<IFileParser>();
    mockFileParse.Setup(m => m.ParseFirstTwoRows(It.IsAny<FileQueue>(), out first, out second)).Verifiable();
    

    现在,当您尝试测试依赖于 ParseFirstTwoRows 的函数时,mock 将为 out 参数提供两个指定的变量。

    【讨论】:

      【解决方案3】:

      我看到您通过使用 out 参数而不是 ref 参数解决了您的问题。

      但是更改生产代码不是解决方案,您可以使用 Typemock 隔离器,它允许使用 ref 参数创建测试:

      [TestMethod,Isolated]
      public void Mocking_With_Ref_Arguments()
      {
          // Arrange
          string[] first = { "'one'", "'two'", "'three'" };
          string[] second = { "'one'", "'two'", "'three'" };
          FileQueue file = new FileQueue();
          var classUnderTest = new Class1();
      
          // What you will set as ref parameters in this line will return as you call the method
          Isolate.WhenCalled(() => classUnderTest.ParseFirstTwoRows(file, ref first, ref second)).IgnoreCall();
      
          // Act
          string[] retFirst = null;
          string[] retSecond = null;
          classUnderTest.someMethod(file, ref retFirst, ref retSecond);
      
          // Assert
          CollectionAssert.AreEquivalent(first, retFirst);
          CollectionAssert.AreEquivalent(second, retSecond);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-02
        • 1970-01-01
        • 2017-01-10
        • 1970-01-01
        • 2015-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多