使用对象参数验证 Moq

我试图验证一个类的参数。正在测试的代码没有问题。漏洞在测试中。

我试过两种方法,都失败了。

以下是我的尝试:

1:

this.MockImageResizeFilter.Verify(m => m.Filter(this.UploadedFileData, new ImageFilterOptions()
{
Width = 256,
Height = 256,
}));

即使作为第二个参数传递的对象具有相同的属性,这也总是会失败。验证了第一个参数的正确性。

2:

this.MockImageResizeFilter.Setup(m => m.Filter(It.IsAny<byte[]>(), It.IsAny<ImageFilterOptions>()))
.Callback<byte[], ImageFilterOptions>((data, options) =>
{
Assert.AreEqual(this.UploadedFileData, data, "data");
Assert.AreEqual(filterOptions.Width, options.Width, "Width");
Assert.AreEqual(filterOptions.Height, options.Height, "Height");
}
);

这种情况总是会过去的,即使在应该失败的时候也是如此。回调中的断言确实会失败,但是异常不会传递到外部上下文,因此测试总是会通过。

你能帮我找出我做错了什么吗?

42899 次浏览

The first attempt is closer to what you want to achieve.

The reason it fails is that Moq (probably) uses Object.Equals under the cover to test if the ImageFilterOptions parameter that the method was called with is the same instance as the one you supplied in the call to Verify.

It is impossible for them to be the same instance, because in Verify you create a new ImageFilterOptions().

Instead of performing the parameter check this way, you could use Moq's It.Is syntax to provide an expression that verifies the parameter was the expected one.

In your case, you want to check that the parameter is of type ImageFilterOptions and that both the Width and the Height are set to 256. The expression that allows you to do that is:

It.Is<ImageFilterOptions>(p => p.Width == 256 && p.Height == 256)

So, your call to Verify could look like this:

this.MockImageResizeFilter.Verify(m => m.Filter(
this.UploadedFileData,
It.Is<ImageFilterOptions>(p => p.Width == 256 && p.Height == 256)));

Moq's Verify method only tells you that the method was never called with the arguments you specified, with no explanation of which argument (or which property of an argument) was wrong. To get fine detail, use a callback to save the argument(s) to a variable and then assert against that:

ImageFilterOptions passedOptions = null;
this.MockImageResizeFilter.Setup(m => m.Filter(It.IsAny<byte[]>(), It.IsAny<ImageFilterOptions>()))
.Callback<byte[], ImageFilterOptions>((data, options) =>
{
passedOptions = options
});


// <exercise the mocked object>


this.MockImageResizeFilter.Verify(m => m.Filter(this.UploadedFileData, It.IsAny<ImageFilterOptions>());
Assert.AreEqual(expectedOptions.Width, passedOptions.Width, "Width");
Assert.AreEqual(expectedOptions.Height, passedOptions.Height, "Height");


// (If you wanted, you could also use the callback technique to check that
// `this.UploadedFileData` was passed in for the first parameter.)

This tells you exactly which argument/property was wrong.

You also get the opportunity to skip testing each property individually, which is nice when dealing with objects with many properties:

using FluentAssertions;


// ...


passedOptions.Should().BeEquivalentTo(expectedOptions);

(Note that BeEquivalentTo does point out individual property failures.)