当我 运行 同时在移动设备和网站上进行自动化测试时,如何只捕获一个屏幕截图?
How do I capture only one screenshot when I run an Automation Test on mobile also on website at the same time?
我无法在 google 或 Whosebug 上为我的场景找到解决方案,我被卡住了。
对于自动化测试,我使用 InteliJ(如 IDE)、java、Selenium、Appium 和 TestNG。
我在网站上执行了初始化移动设备的操作,然后自动执行了移动设备上的操作。
测试失败时截图截取网站和手机屏幕
我只需要捕获与失败的测试操作相关的屏幕。
请看代码:
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
注释的方法通常在与测试方法相同的线程中调用本身), 基于此, 我们现在可以决定是否需要调用捕获浏览器屏幕截图的驱动程序 window 或捕获模拟设备屏幕截图的驱动程序:
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);
}
}
更合适的解决方案很可能需要更改 hundreds/thousands 测试(这很可能是不需要的)。理想情况下,测试用例步骤(动作)应该被更恰当地建模,而不是仅仅存在于我们的想象中 "things" 被 reportLog
方法调用分隔。
Java毕竟是OOP语言
我无法在 google 或 Whosebug 上为我的场景找到解决方案,我被卡住了。
对于自动化测试,我使用 InteliJ(如 IDE)、java、Selenium、Appium 和 TestNG。
我在网站上执行了初始化移动设备的操作,然后自动执行了移动设备上的操作。
测试失败时截图截取网站和手机屏幕
我只需要捕获与失败的测试操作相关的屏幕。
请看代码:
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
注释的方法通常在与测试方法相同的线程中调用本身), 基于此, 我们现在可以决定是否需要调用捕获浏览器屏幕截图的驱动程序 window 或捕获模拟设备屏幕截图的驱动程序:
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);
}
}
更合适的解决方案很可能需要更改 hundreds/thousands 测试(这很可能是不需要的)。理想情况下,测试用例步骤(动作)应该被更恰当地建模,而不是仅仅存在于我们的想象中 "things" 被 reportLog
方法调用分隔。
Java毕竟是OOP语言