C# 在.Net中,System.Drawing.Image.Save是否确定?

C# 在.Net中,System.Drawing.Image.Save是否确定?,c#,image,unit-testing,deterministic,C#,Image,Unit Testing,Deterministic,我试图通过它们的字节内容来比较两个图像。然而,它们并不匹配 两幅图像都是从相同的源图像生成的,使用相同的方法和相同的参数。我猜图像生成或转换为字节数组的方式是不确定的。是否有人知道非确定性行为发生在哪里,以及我是否可以在单元测试中强制执行确定性行为 我的测试类中的这个方法将图像转换为字节数组-是image.Savedeterministic吗?memStream.ToArray()是否具有确定性 专用静态字节[]ImageToByteArray(图像) { 字节[]实际字节; 使用(Memory

我试图通过它们的字节内容来比较两个图像。然而,它们并不匹配

两幅图像都是从相同的源图像生成的,使用相同的方法和相同的参数。我猜图像生成或转换为字节数组的方式是不确定的。是否有人知道非确定性行为发生在哪里,以及我是否可以在单元测试中强制执行确定性行为

我的测试类中的这个方法将图像转换为字节数组-是
image.Save
deterministic吗?
memStream.ToArray()是否具有确定性

专用静态字节[]ImageToByteArray(图像)
{
字节[]实际字节;
使用(MemoryStream memStream=new MemoryStream())
{
保存(memStream,ImageFormat.Bmp);
actualBytes=memStream.ToArray();
}
返回实际字节;
}
下面是失败的单元测试-
TestImageLandscapeSeatterResized_300_300
是使用
ImageHelper.ResizeImage(TestImageLandscapeSeatter,300300)
TestImageLandscapeSeatter
生成的,然后在加载到项目的资源文件之前保存到文件中。如果我的代码中的所有调用都是基于我的输入参数确定的,那么这个测试应该通过

public void ResizeImage\u scape\u SmallerLandscape()
{
Image testImageLandscape=Resources.testImageLandscape沙漠;
Image expectedImage=Resources.TestImageLandscape已调整大小\u 300\u 300;
字节[]expectedBytes=ImageToByteArray(expectedImage);
字节[]实际字节;
使用(Image resizedImage=ImageHelper.ResizeImage(testImageLandscape,300300))
{
实际字节=ImageToByteArray(resizedImage);
}
IsTrue(expectedBytes.SequenceEqual(actualBytes));
}
正在测试的方法-此方法将缩小输入图像,使其高度和宽度小于
maxHeight
maxWidth
,保留现有的纵横比。一些图形调用可能是不确定的,我无法从微软有限的文档中分辨出来

公共静态图像大小图像(图像图像,int-maxWidth,int-maxHeight)
{
十进制宽度=图像宽度;
十进制高度=图像高度;
十进制新宽度;
十进制新高度;
//计算新的宽度和高度
如果(宽度>最大宽度| |高度>最大高度)
{
//需要保留原始纵横比
十进制原始比例=宽度/高度;
十进制宽度缩减因子=最大宽度/宽度;
十进制高度折减系数=最大高度/高度;
if(宽度折减系数<高度折减系数)
{
newWidth=maxWidth;
新高度=新宽度/原始光谱比率;
}
其他的
{
newHeight=maxHeight;
新宽度=新高度*原始光谱比率;
}
}
其他的
//如果小于允许的宽度和高度,则返回图像的副本
返回新位图(图像);
//调整图像大小
位图位图=新位图((int)newWidth,(int)newHeight,PixelFormat.Format48bppRgb);
Graphics graphic=Graphics.FromImage(位图);
graphic.InterpolationMode=InterpolationMode.HighQualityBicubic;
graphic.DrawImage(图像,0,0,(int)newWidth,(int)newHeight);
graphic.Dispose();
返回位图;
}

这最终奏效了。我不知道这对于单元测试来说是否是一个好主意,但是由于GDI+逻辑是非确定性的(或者我的逻辑与之交互),这似乎是最好的方法

我使用MS Fakes填充特性填充依赖调用,并验证预期值是否传递给被调用的方法。然后,我调用本机方法,以获得测试中方法其余部分所需的功能。最后,我验证了返回图像的一些属性

尽管如此,我还是更愿意直接比较预期产出和实际产出

[TestMethod]
[TestCategory(“ImageHelper”)]
[TestCategory(“ResizeImage”)]
public void ResizeImage_landscapetool arge_SmallerLandscape()
{
Image testImageLandscape=Resources.testImageLandscape沙漠;
const int HEIGHT=300;
常数int宽度=300;
const int预期宽度=宽度;
常数int预期高度=(int)(预期宽度/(1024m/768m));
常量PixelFormat应为\u格式=PixelFormat.Format48bppRgb;
bool calledBitMapConstructor=false;
bool calledGraphicsFromImage=false;
bool calledGraphicsDrawImage=false;
使用(ShimsContext.Create())
{
ShimBitmap.ConstructorInt32Int32PixelFormat=(实例,w,h,f)=>{
calledBitMapConstructor=true;
Assert.AreEqual(预期宽度,w);
断言.AreEqual(预期高度,h);
Assert.AreEqual(预期的_格式,f);
ShimsContext.ExecuteWithout垫片(()=>{
ConstructorInfo constructor=typeof(位图).GetConstructor(新[]{typeof(int)、typeof(int)、typeof(PixelFormat)});
Assert.IsNotNull(构造函数);
Invoke(实例,新对象[]{w,h,f});
});
};
ShimGraphics.FromImageImage=i=>{
calledGraphicsFromImage=true;
Assert.IsNotNull(i);
返回ShimsContext.ExecuteWithout垫片(()=>Graphics.FromImage(i));
};
ShimGraphics.AllInstances.DrawImageImageInt32Int32Int32=(实例,i,x,y,w,h)=>{
calledGraphicsDrawImage=true;
Assert.IsNotNull(i);
断言.AreEqual(0,x);
断言.AreEqual(0,y);
Assert.AreEqual(预期宽度,w);
断言.AreEqual(预期高度,h);
ShimsContext.ExecuteWithoutShimps(()=>instance.DrawImage(i,x,y,w,h));
};
使用(Image resizedImage=ImageHelper.Resize
private static bool CompareImages(string source, string expected)
{
    var image1 = new Bitmap($".\\{source}");
    var image2 = new Bitmap($".\\Expected\\{expected}");

    var converter = new ImageConverter();
    var image1Bytes = (byte[])converter.ConvertTo(image1, typeof(byte[]));
    var image2Bytes = (byte[])converter.ConvertTo(image2, typeof(byte[]));

    // ReSharper disable AssignNullToNotNullAttribute
    var same = image1Bytes.SequenceEqual(image2Bytes);
    // ReSharper enable AssignNullToNotNullAttribute

    return same;
}