WebDriver 之测试失败自动截图.docx
《WebDriver 之测试失败自动截图.docx》由会员分享,可在线阅读,更多相关《WebDriver 之测试失败自动截图.docx(9页珍藏版)》请在冰豆网上搜索。
WebDriver之测试失败自动截图
WebDriver之测试失败自动截图
webDriver 测试的时候最头疼的就是调试。
但也远不及运行的时候出错,再回头调试来的痛苦。
总所周知, web 自动化的代码都非常脆弱,一份代码一会运行失败,一会运行成功也是很正常的事情。
总的来说造成案例运行失败的原因大抵有两点:
环境问题:
比如网络不稳定啊
代码变动:
比如某个元素不在
遇到 bug :
这就是真的发现 bug 了
无论哪一种,遇到了都需要花一番时间去debug。
那如果这个时候有一张运行时候出错的截图,那就一目了然了。
(即便不一目了然,也有很多帮助)
在运行出错的时候,捕获错误并截图有两种思路:
自定义一个WeDdriver的监听器,在出异常的时候截图。
利用Juint的TestRule,自定义一个Rule在运行失败的时候截图。
自定义监听器
截图的原理
截图需要用到RemoteWebDriver。
在 Selenium 官方我们可以找到:
Onenicefeatureoftheremote
webdriveristhatexceptionsoften
haveanattachedscreenshot,encoded
asaBase64PNG.Inordertogetthis
screenshot,youneedtowritecode
similarto:
publicStringextractScreenShot(WebDriverExceptione){
Throwablecause=e.getCause();
if(causeinstanceofScreenshotException){
return((ScreenshotException)cause).getBase64EncodedScreenshot();
}
returnnull;
}
意思就是说,每个异常都是ScreenshotException的对象,转码一下就可以用了。
这是截图的本质。
import java.io.File;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.text.SimpleDateFormat;
importjava.util.Date;
importorg.openqa.selenium.WebDriver;
importorg.openqa.selenium.internal.Base64Encoder;
importorg.openqa.selenium.remote.ScreenshotException;
importorg.openqa.selenium.support.events.AbstractWebDriverEventListener;
/**
*Thisisancustomizedwebdrivereventlistener.
*NowitimplementsonExceptionmethod:
webdriverwilltakeascreenshot
*whenitmeetsanexception.It'sgoodbutnotsousable.Andwhenweuse
*WebDriverWaittowaitforanelementappearing,thewebdriverwillthrow
*exceptionalwaysandtheonExceptionwillbeexcutedagainandagain,which
*generatesalotofscreenshots.
*Puthereforstudy
*Usage:
*WebDriverdriver=newFirefoxDriver();
*WebDriverEventListenerlistener=newCustomWebDriverEventListener();
*returnnewEventFiringWebDriver(driver).register(listener);
*
*@authorqa
*
*/
publicclassCustomWebDriverEventListenerextends
AbstractWebDriverEventListener{
@Override
publicvoidonException(Throwablethrowable,WebDriverdriver){
Throwablecause=throwable.getCause();
if(causeinstanceofScreenshotException){
SimpleDateFormatformatter=newSimpleDateFormat(
"yyyy-MM-dd-hh-mm-ss");
StringdateString=formatter.format(newDate());
Fileof=newFile(dateString+"-exception.png");
FileOutputStreamout=null;
try{
out=newFileOutputStream(of);
out.write(newBase64Encoder()
.decode(((ScreenshotException)cause)
.getBase64EncodedScreenshot()));
}
catch(Exceptione){
e.printStackTrace();
}
finally{
try{
out.close();
}
catch(IOExceptione){
e.printStackTrace();
}
}
}
}
}
主要看onException这个方法的实现,很明显,我们捕获了这个异常,然后通过强制转换将图片提取出来,写入硬盘。
然后就是使用这个监听器,通常会在setup方法里面将这个监听器注册到WebDriver中去,看代码:
@Test
publicvoidsetup(){
Stringremote_driver_url="http:
//localhost:
4444/wd/hub";
DesiredCapabilitiescapability=null;
capability=DesiredCapabilities.firefox();
WebDriverEventListenereventListener=newCustomWebDriverEventListener();
WebDriverdriver=newEventFiringWebDriver(newRemoteWebDriver(newURL(
remote_driver_url),capability)).register(eventListener);
}
在这之后,如果运行出错,WebDriver抛出异常就会在相应的classpath下面生成png的截图。
自定义TestRule
和自定义WebDriver监听器不同,自定义TestRule只有在这个Rule被执行的时候,才去做一些我们预设的CallBack。
所以这个截图动作,对于WebDriver而言,是主动的。
那么,我们就需要自定义一个RemoteWebDriver来实现截图功能。
WebDriver自身提供了TakesScreenshot这个接口,我们只要实现它就可以了,看代码:
import.URL;
importorg.openqa.selenium.OutputType;
importorg.openqa.selenium.TakesScreenshot;
importorg.openqa.selenium.WebDriverException;
importorg.openqa.selenium.remote.CapabilityType;
importorg.openqa.selenium.remote.DesiredCapabilities;
importorg.openqa.selenium.remote.DriverCommand;
importorg.openqa.selenium.remote.RemoteWebDriver;
publicclassCustomRemoteWebDriverextendsRemoteWebDriverimplements
TakesScreenshot{
publicCustomRemoteWebDriver(URLurl,DesiredCapabilitiesdc){
super(url,dc);
}
@Override
publicXgetScreenshotAs(OutputTypetarget)
throwsWebDriverException{
if((Boolean)getCapabilities().getCapability(
CapabilityType.TAKES_SCREENSHOT)){
returntarget
.convertFromBase64Png(execute(DriverCommand.SCREENSHOT)
.getValue().toString());
}
returnnull;
}
}
然后,我们在加一个封装类,将截图方法放进去。
WebDriverWrapper.screenShot:
/**
*Functiontotakethescreenshotandsaveittotheclasspathdir.
*Usually,youwillfindthepngfileundertheprojectroot.
*
*@paramdriver
*Webdriverinstance
*@paramdesc
*Thedescriptionofthepng
*/
publicstaticvoidscreenShot(WebDriverdriver,Stringdesc){
DatecurrentTime=newDate();
SimpleDateFormatformatter=newSimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
StringdateString=formatter.format(currentTime);
FilescrFile=((TakesScreenshot)driver)
.getScreenshotAs(OutputType.FILE);
try{
desc=desc.trim().equals("")?
"":
"-"+desc.trim();
Filescreenshot=newFile("screenshot"+File.separator
+dateString+desc+".png");
FileUtils.copyFile(scrFile,screenshot);
}catch(IOExceptione){
e.printStackTrace();
}
}
下面,就是添加Junit的TestRule:
importorg.junit.rules.TestRule;
importorg.junit.runner.Description;
importorg.junit.runners.model.Statement;
importorg.openqa.selenium.WebDriver;
publicclassTakeScreenshotOnFailureRuleimplementsTestRule{
privatefinalWebDriverdriver;
publicTakeScreenshotOnFailureRule(WebDriverdriver){
this.driver=driver;
}
@Override
publicStatementapply(finalStatementbase,Descriptiondescription){
returnnewStatement(){
@Override
publicvoidevaluate()throwsThrowable{
try{
base.evaluate();
}
catch(Throwablethrowable){
WebDriverWrapper.screenShot(driver,"assert-fail");
throwthrowable;
}
}
};
}
}
代码很简单,在抛出evalate方法的错误之前,截图。
然后就是使用这个TestRule,很简单,只要在你的 测试用例里面加入:
publicclassMyTest{
...
@Rule
publicTestRulemyScreenshot=newTakeScreenshotOnFailureRule(driver);
...
@Test
publicvoidtest1(){}
@Test
publicvoidtest2(){}
...
}
即可。
关于Junit的Rule请自行google!
两则的比较
总得来说,两种方法都很方便,也很有效果,基本都能截图成功。
不同之处在于,
RemoteWebDriver监听器是在RemoteWebDriver抛出异常的时候截图。
TestRule是在assert失败的时候截图。
我在项目中最早是用第一种方法,后来改用第二种,主要是因为,在自定义的监听器里,它遇到所有的异常都会截图,这个时候,如果你用了conditionwait一个Ajax的元素,那就会很悲剧,你会发现在你的目录下面有无数的截图。
当初我没有找到解决方法,期待有人提出。