Moq:在测试中从ref参数中获取值

时间:2016-06-20 16:32:16

标签: c# unit-testing moq

鉴于此功能参考

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 []&amp;,String []&amp;)无法使用参数调用回调   (开fileQueue,字符串[],字符串[])。

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

3 个答案:

答案 0 :(得分:0)

您不需要回电话。只需确保在调用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,了解它的功能。

答案 1 :(得分:0)

我通过做一些重构来解决这个问题。虽然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参数提供两个指定的变量。

答案 2 :(得分:0)

我看到你通过使用参数而不是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);
}