65.9K
CodeProject 正在变化。 阅读更多。
Home

截图冒烟测试

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.63/5 (8投票s)

2013年11月28日

CPOL

5分钟阅读

viewsIcon

37747

downloadIcon

504

利用 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 - 已批准页面的 URL
    • ActiveURL - 正在测试页面的 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 图像添加了一个链接到错误消息中,以便审查测试的人可以单击链接查看发生的问题。

© . All rights reserved.