Java 当我同时在手机和网站上运行自动化测试时,如何只捕获一个屏幕截图?
我无法在谷歌或stackoverflow上为我的场景找到解决方案,我被卡住了 对于自动化测试,我使用InteliJ(作为IDE)、java、Selenium、Appium和TestNG 我在网站上执行了初始化手机的操作,然后自动化在手机上执行操作 测试失败时的屏幕截图将捕获网站和移动屏幕 我只需要捕获与失败的测试操作相关的屏幕 请参阅代码:Java 当我同时在手机和网站上运行自动化测试时,如何只捕获一个屏幕截图?,java,selenium,automated-tests,testng,appium,Java,Selenium,Automated Tests,Testng,Appium,我无法在谷歌或stackoverflow上为我的场景找到解决方案,我被卡住了 对于自动化测试,我使用InteliJ(作为IDE)、java、Selenium、Appium和TestNG 我在网站上执行了初始化手机的操作,然后自动化在手机上执行操作 测试失败时的屏幕截图将捕获网站和移动屏幕 我只需要捕获与失败的测试操作相关的屏幕 请参阅代码: public abstract class BaseTest implements ITest, V3RestApi, V2Api { private b
public abstract class BaseTest implements ITest, V3RestApi, V2Api {
private boolean isMobileAppLaunched = false;
@AfterMethod
public void afterMainMethod(ITestResult result) {
try {
if (result.getStatus() == ITestResult.FAILURE) {
captureScreenshot(result);
}
driver.quit();
if (isMobileAppLaunched) {
this.closeAppiumSession();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void captureScreenshot(ITestResult result) {
try {
String screenshotName;
File screenshot;
screenshotName = Utilities.getFileName(result.getName());
screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
this.attachScreenShotWithReport(screenshotName, screenshot, result);
if (isMobileAppLaunched) {
screenshotName = Utilities.getFileName(result.getName());
screenshot = ((TakesScreenshot) appiumDriver).getScreenshotAs(OutputType.FILE);
this.attachScreenShotWithReport(screenshotName, screenshot, result);
}
} catch (Exception e) {
logger.warn("Screenshot could not be captured for " + result.getName());
}
}
public void launchMobileApplication(MobileType mobileApplicationType) throws Exception {
this.isMobileAppLaunched = true;
}
}
public class AndroidTestCase extends BaseTest {
@Test(description = "Test description"})
public void testCaseOnAndroid() throws Exception {
reportLog("Login into the application as User Name");
//login action to website;
reportLog("Click on Hamburger Menu");
//click action on the website;
reportLog("Activate to recognize the mobile"));
//action on site to recognize the mobile;
reportLog("Mobile: Launch Mobile Application");
//launch the mobile;
reportLog("Mobile: Login into the Mobile application as User Name");
//action to login;
reportLog("Mobile: Click on tab");
//action on Mobile;
}
}
假设你们通过记录一条前缀为“mobile:”的消息来区分移动操作和web操作,并且
reportLog
方法总是在与测试方法本身相同的线程中调用(例如,testCaseOnAndroid
),无论何时调用reportLog
,我们都可以构建一个缓存,用于保存给定线程(测试用例)的最后一次尝试操作。如果测试用例失败并且调用了afterTestCase
,我们可以检查缓存并获取当前线程的最后一次尝试操作(通常在与测试方法本身相同的线程中调用带有@AfterMethod
注释的方法),基于此,现在,我们可以决定是否需要调用捕获浏览器窗口屏幕截图的驱动程序或捕获模拟设备屏幕截图的驱动程序:
public abstract class BaseTest {
/**
* Defines the type of a reported test action.
*/
public enum ReportedActionType {
MOBILE,
WEB
}
private final ConcurrentHashMap<Long, ReportedActionType> lastAttemptedActionCache = new ConcurrentHashMap<>();
@AfterMethod
public void afterTestCase(final ITestResult testResult) {
final Long currentThreadId = currentThread().getId();
final ReportedActionType lastReportedActionType = this.lastAttemptedActionCache.get(currentThreadId);
if (testResult.getStatus() == FAILURE) {
printToConsole(String.format("Test failed while attempting to perform a '%1$s' action. | %2$s",
lastReportedActionType,
testResult.getName()));
try {
if (lastReportedActionType == MOBILE) {
captureEmulatedMobileDevice(testResult);
} else {
captureBrowserWindow(testResult);
}
} catch (final Exception exception) {
exception.printStackTrace();
}
}
// todo: quit web driver (Selenium)
// todo: quit mobile driver (close Appium session)
// irrespective of the state of the test result (success or failure), we need to make sure that we
// remove the cached information, otherwise the cache can get really
// large and this could lead to out of memory problems (we could potentially consider
// using a more sophisticated cache implementation of a 3rd-party library
// that supports time-based eviction, so that even if we forget to remove the
// cached information manually, it gets removed automatically after a fixed amount of time - e.g., 5-10 seconds)
this.lastAttemptedActionCache.remove(currentThreadId);
}
// todo: call the appropriate driver to capture a screenshot of the emulated device
private void captureEmulatedMobileDevice(final ITestResult testResult) {
printToConsole("Screenshot of the emulated mobile device has been captured. | " + testResult.getName());
}
// todo: call the appropriate driver to capture a screenshot of the browser window
private void captureBrowserWindow(final ITestResult testResult) {
printToConsole("Screenshot of the browser has been captured. | " + testResult.getName());
}
public void reportLog(final String message) {
// log the message (to console, to file, etc.)
printToConsole(message);
// the assumption is that the actions within a test case are executed within the same
// thread the test case itself is executed in; as long as this assumption holds, we can cache
// the needed information and fetch it later to perform the needed checks
this.lastAttemptedActionCache.put(currentThread().getId(),
getReportedActionType(message));
}
private ReportedActionType getReportedActionType(final String reportLogMessage) {
return reportLogMessage.toLowerCase()
.trim()
.startsWith("mobile:") ? MOBILE : WEB;
}
// todo: replace this with a proper logger
private void printToConsole(final String message) {
System.out.println(currentThread().getId() + " | " + this.getClass()
.getSimpleName() + " | " + message);
}
}
公共抽象类BaseTest{
/**
*定义报告的测试操作的类型。
*/
公共枚举ReportedActionType{
可移动的
网状物
}
私有最终ConcurrentHashMap lastAttemptedActionCache=新ConcurrentHashMap();
@后置法
公共无效后测试案例(最终ITestResult测试结果){
最终长currentThreadId=currentThread().getId();
final ReportedActionType lastReportedActionType=this.lastAttemptedActionCache.get(currentThreadId);
if(testResult.getStatus()==失败){
printToConsole(String.format(“尝试执行“%1$s”操作时测试失败。|%2$s”,
lastReportedActionType,
testResult.getName());
试一试{
如果(lastReportedActionType==MOBILE){
CaptureSimulatedMobileDevice(测试结果);
}否则{
captureBrowserWindow(测试结果);
}
}捕获(最终异常){
异常。printStackTrace();
}
}
//todo:退出web驱动程序(Selenium)
//todo:退出移动驱动程序(关闭应用程序会话)
//无论测试结果的状态如何(成功或失败),我们都需要确保
//删除缓存的信息,否则缓存可能会
//大,这可能导致内存不足的问题(我们可能会考虑)
//使用第三方库的更复杂的缓存实现
//这支持基于时间的驱逐,因此即使我们忘记移除
//手动缓存的信息,在固定时间(例如5-10秒)后自动删除)
this.lastAttemptedActionCache.remove(currentThreadId);
}
//todo:调用相应的驱动程序以捕获模拟设备的屏幕截图
私有无效CaptureMulatedMobileDevice(最终ITestResult测试结果){
printToConsole(“已捕获模拟移动设备的屏幕截图。|“+testResult.getName());
}
//todo:调用相应的驱动程序以捕获浏览器窗口的屏幕截图
私有void captureBrowserWindow(最终ITestResult测试结果){
printToConsole(“已捕获浏览器的屏幕截图。|“+testResult.getName());
}
公共void报告日志(最终字符串消息){
//记录消息(到控制台、文件等)
printToConsole(消息);
//假设测试用例中的操作是在同一个测试用例中执行的
//测试用例本身是在线程中执行的;只要这个假设成立,我们就可以缓存
//获取所需的信息,并稍后获取以执行所需的检查
this.lastAttemptedActionCache.put(currentThread().getId(),
getReportedActionType(消息));
}
私有ReportedActionType getReportedActionType(最终字符串reportLogMessage){
return reportLogMessage.toLowerCase()返回
.trim()
.startsWith(“mobile:”)?mobile:WEB;
}
//todo:将其替换为正确的记录器
私有void printToConsole(最终字符串消息){
System.out.println(currentThread().getId()+“|”+this.getClass()
.getSimpleName()+“|”+消息);
}
}
一个更合适的解决方案很可能需要更改成百上千的测试(这很可能是不需要的)。理想情况下,测试用例步骤(操作)应该更恰当地建模,而不仅仅存在于我们的想象中,作为由reportLog
方法调用分隔的“事物”
Java毕竟是一种OOP语言。假设你们通过记录一条前缀为“mobile:”的消息来区分移动操作和web操作,并且
reportLog
方法总是在与测试方法本身相同的线程中调用(例如,testCaseOnAndroid
),无论何时调用reportLog
,我们都可以构建一个缓存,用于保存给定线程(测试用例)的最后一次尝试操作。如果测试用例失败并且调用了afterTestCase
,我们可以检查缓存并获取当前线程的最后一次尝试操作(用@AfterMe注释的方法)