截图冒烟测试






4.63/5 (8投票s)
利用 Selenium 和 Image Magick 进行屏幕截图比较,以执行 UI 回归测试。
引言
我们正在利用 Selenium 2 进行网页功能测试。如果您不熟悉 Selenium,它基本上用于自动化 Web 操作,可以单独使用或在脚本中使用。您可以使用 Firefox 插件来录制您的操作步骤并将其导出为 junit (Java)、rspec (ruby) 或 nunit (C#) 单元测试。我开始编写一些基本的 Selenium 测试,以确保代码提交不会破坏解决方案中的其他内容。一些简单的操作,例如确保页面加载并存在一些项目。测试中的检查越多,您对冒烟测试中一切就绪的信心就越强。这让我想到,最好能够让测试直观地比较新提交的页面与提交之前的页面,并确保一切正常。
背景
ImageMagick
具有许多功能,但我们将重点关注其比较功能。如果我们截取页面处于我们喜欢的状态下的屏幕截图,并将其与新构建的页面进行比较,我们就可以利用 ImageMagick
的比较功能来告诉我们是否有问题。由于某种原因,可能会有一些轻微的像素偏移是可以接受的,但我们可以调整比较设置,当差异达到一定水平时就失败。我看到了这篇文章,它给了我这个想法:Stack Overflow 帖子。在这里,您可以看到 PAE 度量标准的使用——即峰值平均误差,它似乎效果很好。Magick.net 在 CodePlex 上提供了一个不错的封装,使得在 .NET 项目中使用 ImageMagick 更加容易。
Using the Code
![]() |
VisualSmoke 解决方案仅用于演示此方法,它包含一个标准的 ASP.NET MVC Razor 2 项目,名为 VisualSmoke.Web ,这是我们的测试项目将访问的 Web 应用程序。VisualSmoke.Web.Tests 项目是本文的重点。 |
请注意,VisualSmoke.Web.Tests
项目有几个 NuGet 包
- NUnit - NUnit 是一个面向所有 .NET 语言的单元测试框架,具有强大的 TDD 功能。
- Selenium packages (4) - 允许启动 Selenium 服务器并在我们的单元测试中利用它
- Magick.NET-Q16-x86 - 用于桌面和 Web 的
ImageMagick
图像处理库的 .NET API。
测试概述
SetupTest()
设置一些将在测试期间使用的属性,以便测试知道
- 它应该访问哪些 URL
- 它应该读取和写入屏幕截图的目录
- 调用
PopulateUrls()
方法,我们将在下一步详细介绍 - 启动 Selenium 服务器,以便我们可以运行基于 Selenium 的测试
[SetUp]
public void SetupTest()
{
ActiveShots = ConfigurationManager.AppSettings["activeShotsPath"];
ApprovedShots = ConfigurationManager.AppSettings["approvedShotsPath"];
ErroredShots = ConfigurationManager.AppSettings["erroredShotsPath"];
DeltaShots = ConfigurationManager.AppSettings["deltaShotsPath"];
BaseDomain = string.Format("http://{0}/",
ConfigurationManager.AppSettings["domain"]);
CompareDomain = string.Format("http://{0}/",
ConfigurationManager.AppSettings["compareDomain"]);
PopulateUrls();
selenium = new DefaultSelenium("localhost", 4444, "*chrome",
string.Format("http://{0}/", ConfigurationManager.AppSettings["domain"]));
selenium.Start();
verificationErrors = new StringBuilder();
}
PopulateUrls()
- 读取 ApprovedPages.txt 文件,其中包含我们应该进行屏幕截图冒烟测试的页面列表。
- 我们有一个简单的 "
Page
" 类,它包含以下属性,以便更轻松地处理数据名称
ApprovedURL
- 已批准页面的 URLActiveURL
- 正在测试页面的 URL
private void PopulateUrls()
{
UrlList = new List<page>();
const string file = "VisualSmoke.Web.Tests.Selenium.ApprovedPages.txt";
using (var tr = GetInputFile(file))
{
string line;
while ((line = tr.ReadLine()) != null)
{
var page = new Page();
page.Name = line;
if (line == "home")
{
page.ApprovedUrl = CompareDomain;
page.ActiveUrl = BaseDomain;
}
else
{
page.ApprovedUrl = CompareDomain + line;
page.ActiveUrl = BaseDomain + line;
}
UrlList.Add(page);
}
}
}
ApprovedPages.txt
ApprovedPages.txt 文件允许控制哪些页面应该进行冒烟测试,而无需为每个页面创建单元测试。
*注意* - ApprovedPages.txt 文件的属性设置为嵌入资源,这使得在将文件推送到 CI 环境时,其位置无关紧要。
home
products
solutions
about-us
ScreenshotCompareTest()
- 如果 "
baseline
" 的AppSetting
为true
,则ScreenshotCompareTest()
将捕获已批准页面的基线进行比较。 - 然后,它将遍历
UrlList
中的页面进行测试,然后使用Selenium.CaptureEntirePageScreenshot
捕获并保存屏幕截图,利用我们的CaptureImagePath
方法。 - 然后,我们使用
CompareImage
方法将捕获的图像与已批准的对应图像进行比较 CompareImage
将返回Match
、NoMatch
或Error
- 如果出现
NoMatch
,我们将在verificationErrors StringBuilder
中记下它,以便在测试结束时的Assert
消息中使用。
[Test]
public void ScreenshotCompareTest()
{
if (ConfigurationManager.AppSettings["baseline"] == "true")
{
CaptureApprovedPages();
}
foreach (var url in UrlList)
{
selenium.Open(url.ActiveUrl);
selenium.WaitForPageToLoad("30000");
selenium.CaptureEntirePageScreenshot(CaptureImagePath(url.Name), "");
var result = CompareImage(url.Name);
if (result != CompareResult.Match)
{
if (result == CompareResult.Error)
{
verificationErrors.AppendLine(url.Name + " : " +
LastException.Message);
}
else
{
verificationErrors.AppendLine(url.Name + "
did not match the approved screenshot. " + string.Format
("{0}images/screenshots/delta/{1}.png",
UrlList[0].ActiveUrl, url.Name));
}
}
}
}
CaptureApprovedPages()
当环境处于已批准状态,供 QA 和/或业务用户使用时,将 appSetting
的 baseline 设置为 true
。这将遍历 UrlList
,截取屏幕截图并将图像保存到已批准的图像目录。
private void CaptureApprovedPages()
{
foreach (var url in UrlList)
{
selenium.Open(url.ApprovedUrl);
selenium.CaptureEntirePageScreenshot(ApprovedImagePath(url.Name), "");
}
}
CompareImage
CompareImage
方法是执行 ImageMagick
工作的核心方法,用于比较图像。如果比较结果超出可接受范围,我们将差异保存到 delta 文件夹中,供人工在测试失败时进行审查。下面我们使用的是 PeakAbsoluteError
。我仍在尝试调整可接受的范围,甚至 PeakAbsoluteError
是否是正确的度量标准,但我相当确定 1.0 的结果绝对是不可接受的。
private CompareResult CompareImage(string name)
{
var activeImage = new MagickImage(CaptureImagePath(name));
var approvedImage = new MagickImage(ApprovedImagePath(name));
Assert.IsNotNull(activeImage);
Assert.IsNotNull(approvedImage);
var result = CompareResult.NoMatch;
try
{
using (var delta = new MagickImage())
{
var compareResult = activeImage.Compare
(approvedImage, Metric.PeakAbsoluteError, delta);
if (compareResult < .9)
{
result = CompareResult.Match;
}
else
{
delta.Write(string.Format("{0}\\{1}.png", DeltaShots, name));
}
}
}
catch (Exception ex)
{
result = CompareResult.Error;
LastException = ex;
}
return result;
}
图像比较示例
下面,您可以看到一个测试,在截取已批准的屏幕截图后,我更新了主页并添加了额外的文本。这导致主页比较不匹配,并且额外的文本以红色显示在 delta 比较图像中。
更新后的页面

已批准的页面

比较

单元测试失败报告
单元测试通常在失败后停止,但当您进行 Selenium 测试时,您会倾向于继续运行脚本,并将所有问题累积到最后。下面,您可以看到测试结果。我还为 delta 图像添加了一个链接到错误消息中,以便审查测试的人可以单击链接查看发生的问题。