【发布时间】:2015-07-08 19:18:55
【问题描述】:
我正在尝试通过它们的字节内容比较两个图像。但是,它们不匹配。
两个图像都是从同一个源图像生成的,使用相同的方法和相同的参数。我猜测图像生成中的某些内容或我转换为字节数组的方式不是确定性的。有谁知道非确定性行为发生在哪里以及我是否可以轻松地为我的单元测试强制执行确定性行为?
我的测试类中的这个方法将图像转换为字节数组 - image.Save 是确定性的吗? memStream.ToArray() 是确定性的吗?
private static byte[] ImageToByteArray(Image image)
{
byte[] actualBytes;
using (MemoryStream memStream = new MemoryStream())
{
image.Save(memStream, ImageFormat.Bmp);
actualBytes = memStream.ToArray();
}
return actualBytes;
}
这是失败的单元测试 - TestImageLandscapeDesertResized_300_300 是使用 ImageHelper.ResizeImage(testImageLandscape, 300, 300) 从 TestImageLandscapeDesert 生成的,然后在加载到项目的资源文件之前保存到文件中。如果我的代码中的所有调用都基于我的输入参数是确定性的,那么这个测试应该通过。
public void ResizeImage_Landscape_SmallerLandscape()
{
Image testImageLandscape = Resources.TestImageLandscapeDesert;
Image expectedImage = Resources.TestImageLandscapeDesertResized_300_300;
byte[] expectedBytes = ImageToByteArray(expectedImage);
byte[] actualBytes;
using (Image resizedImage = ImageHelper.ResizeImage(testImageLandscape, 300, 300))
{
actualBytes = ImageToByteArray(resizedImage);
}
Assert.IsTrue(expectedBytes.SequenceEqual(actualBytes));
}
正在测试的方法 - 此方法将缩小输入图像,使其高度和宽度小于maxHeight 和maxWidth,保留现有的纵横比。一些图形调用可能是不确定的,我无法从 Microsoft 有限的文档中看出。
public static Image ResizeImage(Image image, int maxWidth, int maxHeight)
{
decimal width = image.Width;
decimal height = image.Height;
decimal newWidth;
decimal newHeight;
//Calculate new width and height
if (width > maxWidth || height > maxHeight)
{
// need to preserve the original aspect ratio
decimal originalAspectRatio = width / height;
decimal widthReductionFactor = maxWidth / width;
decimal heightReductionFactor = maxHeight / height;
if (widthReductionFactor < heightReductionFactor)
{
newWidth = maxWidth;
newHeight = newWidth / originalAspectRatio;
}
else
{
newHeight = maxHeight;
newWidth = newHeight * originalAspectRatio;
}
}
else
//Return a copy of the image if smaller than allowed width and height
return new Bitmap(image);
//Resize image
Bitmap bitmap = new Bitmap((int)newWidth, (int)newHeight, PixelFormat.Format48bppRgb);
Graphics graphic = Graphics.FromImage(bitmap);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.DrawImage(image, 0, 0, (int)newWidth, (int)newHeight);
graphic.Dispose();
return bitmap;
}
【问题讨论】:
-
你可以从你的类中提取位图/图形(例如,使用适配器),这样你就可以测试你的实际逻辑和调用而不是图形组件本身(你不需要测试 - 我们已经知道图形可以正常工作)。
-
该代码已经使用了相当长的一段时间,但我们没有进行太多的单元测试。我知道这是不好的做法,但我现在正试图通过创建单元测试来纠正这种情况。因此,在不更改被测代码的情况下,您是否建议我使用 MS Fakes 来填充图形调用并验证它们是使用预期参数调用的。
-
您可以检查的一件事是 ImageToByteArray 在同一图像上多次调用时是否返回相同的内容。这不是我通常这样做的方式,即在两者上使用 GetPixel 并比较值。它可能会慢一点,但您可以准确报告图像的不同之处。
-
@mikez 我用相同的源图像调用了
ImageToByteArray方法 5 次,间隔 17 秒。将第一个结果与每个后续结果进行比较表明,所有 5 个调用都返回了相同的值。非确定性行为必须在被测方法中发生。 -
我注意到您使用
PixelFormat.Format48bppRgb创建位图,但随后您将其保存为 .bmp。这可能会导致意外行为......当您以这种方式保存图像时,图像的位深度将减少到 24bpp。也许这会影响您的参考图像的保存方式?
标签: c# image unit-testing deterministic