当我 运行 同时在移动设备和网站上进行自动化测试时,如何只捕获一个屏幕截图?

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语言