Moq:从测试中的 ref 参数取回值
Moq: getting values back from ref parameters in test
鉴于此功能参考
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;
但这会导致错误:
Invalid callback. Setup on method with parameters
(FileQueue,String[]&,String[]&) cannot invoke callback with parameters
(FileQueue,String[],String[]).
我已经看到几个关于此的问题,但我很困惑是否我正在尝试做的事情是不可能的,或者我是否只是没有正确地构建测试。我的测试只需要确保 ref 参数在执行代码中具有值。
您不需要回电。只需确保在调用 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 以了解它的功能。
我通过重构解决了这个问题。虽然在这种情况下 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
的函数时,模拟将为 out
参数提供两个指定变量。
我看到您通过使用 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);
}
鉴于此功能参考
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;
但这会导致错误:
Invalid callback. Setup on method with parameters (FileQueue,String[]&,String[]&) cannot invoke callback with parameters (FileQueue,String[],String[]).
我已经看到几个关于此的问题,但我很困惑是否我正在尝试做的事情是不可能的,或者我是否只是没有正确地构建测试。我的测试只需要确保 ref 参数在执行代码中具有值。
您不需要回电。只需确保在调用 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 以了解它的功能。
我通过重构解决了这个问题。虽然在这种情况下 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
的函数时,模拟将为 out
参数提供两个指定变量。
我看到您通过使用 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);
}