当前位置: 首页 > 知识库问答 >
问题:

使用TestNG框架和Log4J为每个测试类保存唯一日志

王楚青
2023-03-14

我有一个自动化项目(Java、Maven with TestNG、log4j)。我试图为每个测试类创建日志文件(LoginTest将创建LoginTest.log,HomeTest将创建HomeTest.log,等等),为此我实现了自己的日志机制,但在运行结束时,我只得到最后一个类测试日志文件(用于运行的最后一个测试类)。

这是我的逻辑:

    public class TestLogger extends LoggerFormat {

    private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of(EnvConf.getDefaultTimeZone());
    private final ThreadLocal<Map<String, List<String>>> testsLogMap = new ThreadLocal<>();

    public void info(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.info(msg);
        log(Level.INFO, context, msg);
    }

    public void error(ITestContext context, Throwable t, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.error(msg, t);
        log(Level.ERROR, context, msg, t);
    }

    public void error(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.error(msg);
        log(Level.ERROR, context, msg);
    }

    public void warn(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.warn(msg);
        log(Level.WARN, context, msg);
    }

    public void warn(ITestContext context, Throwable t, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.warn(msg, t);
        log(Level.WARN, context, msg, t);
    }

    public void debug(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.debug(msg);
        if (LoggerFactory.isDebug()) {
            log(Level.DEBUG, context, msg);
        }
    }

    public void debug(ITestContext context, Throwable t, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.debug(msg, t);
        if (LoggerFactory.isDebug()) {
            log(Level.DEBUG, context, msg, t);
        }
    }

    private void log(Level level, ITestContext context, Object message, Throwable t) {
        message = String.format("%s\n%s", message, throwableToString(t));
        log(level, context, message);
    }

    private void log(Level level, ITestContext context, Object message) {
        Map<String, List<String>> logsMap = getLogsMap();
        if (!logsMap.containsKey(context.getName())) {
            logsMap.put(context.getName(), new ArrayList<>());
        }

        logsMap.get(context.getName()).add(formatMsg(message, level));
        if (level.toInt() != Level.DEBUG.toInt()) {
            Reporter.log((String) message, 0);
        }
    }

    private static String formatMsg(Object message, Level level) {
        LocalDateTime dateTime = LocalDateTime.now(DEFAULT_ZONE_ID);
        return String.format("[%s][%s]%s",
                TIMESTAMP_FORMAT.format(dateTime),
                level, message);
    }

    public List<String> getAndDeleteLogsByTest(String testName) {
        return getLogsMap().remove(testName);
    }

    private Map<String, List<String>> getLogsMap() {
        if (testsLogMap.get() == null) {
            testsLogMap.set(new HashMap<>());
        }
        return testsLogMap.get();
    }


    private static String throwableToString(Throwable t) {
        StringBuilder builder = new StringBuilder();
        for (StackTraceElement traceElement : t.getStackTrace()) {
            builder.append(traceElement.toString()).append('\n');
        }
        return builder.toString();
    }


    private static String handleFormatMsg(String message, Object... args) {
        if (args.length == 0 && message.contains("%")) {
            message = message.replaceAll("%", "%%");
        }
        return String.format(message, args);
    }
}

这是我的基本测试类:

@Listeners({NeoTestListener.class})
public class BaseTest {

    private static final Browser BROWSER = Browser.valueOf(EnvConf.getProperty("ui.browser.type"));
    private static final File SCREENSHOTS_FOLDER = new File(EnvConf.getProperty("test_output.screenshots.folder"));
//    private static final File DOWNLOADS_FOLDER = new File(EnvConf.getProperty("workspace.tests.downloads"));
    private static final File DOWNLOADS_FOLDER = new File(EnvConf.getProperty("test_output.logs.folder"));
    private static final String ADMIN_USERNAME = EnvConf.getProperty("hackeruso.admin.user.email");
    private static final String ADMIN_PASSWORD = EnvConf.getProperty("hackeruso.admin.user.password");

    protected static DriverWrapper driver;
    protected LoginPage loginPage;
    protected Date testStartTime;
    protected ITestContext context;
    protected TopBar topBar;
    protected final File testTempFolder;

    static {
        if (!SCREENSHOTS_FOLDER.exists()) {
            FileUtil.createFolder(SCREENSHOTS_FOLDER, true);
        }

        if(!DOWNLOADS_FOLDER.exists()) {
            FileUtil.createFolder(DOWNLOADS_FOLDER, false);
        }
    }

    protected static String randSuffix(String prefix){
        return prefix + "_" + String.valueOf(System.nanoTime()).substring(9);
    }

    public BaseTest() {
        this.testTempFolder = new File(DOWNLOADS_FOLDER, randSuffix(getClass().getSimpleName()));
        FileUtil.createFolder(testTempFolder, false);
//        testTempFolder.deleteOnExit();
    }

    @BeforeClass
    public final void baseSetup(ITestContext context) throws IOException {
        this.context = context;
        driver = DriverWrapper.open(BROWSER, DOWNLOADS_FOLDER);
        loginPage = new LoginPage(driver);
        info("<!!! '%s' START !!!>" , context.getName());
        testStartTime = new Date();
        this.context.setAttribute("test_start_time", testStartTime);
        info("testStartTime=[%s]" , testStartTime);
    }

    private void printBrowserLog() {

        List<LogEntry> serverLogLines = driver.manage().logs().get(LogType.BROWSER).getAll();
        if (serverLogLines.size() > 0) {
            Log.i("<---------Browser [SERVER] log start--------->");
            for (LogEntry entry : serverLogLines) {
                Log.e(entry.toString());
            }
            Log.i("<---------Browser [SERVER] log end------------>");
        }
    }

    @AfterClass
    public final void baseTeardown(ITestContext context) {
        Date testEndTime = new Date();
        if (driver != null) {
            printBrowserLog();
            driver.quit();
        }
        info("<!!! '%s' END !!!>" , context.getName());
        info("testEndTime=[%s]" , testEndTime);
    }

    public static DriverWrapper getDriver() {
        return driver;
    }

    protected void info(String message , Object...args){
        TESTS_LOG.info(context , message , args);
    }

    public void error(Throwable t , String messageFormat , Object...args) {
        TESTS_LOG.error(context , t , messageFormat , args);
    }

    public void error(String messageFormat , Object...args) {
        TESTS_LOG.error(context , messageFormat , args);
    }

    protected void warn(String messageFormat , Object...args) {
        TESTS_LOG.warn(context , messageFormat , args);
    }

    public void warn(Throwable t ,String messageFormat , Object...args) {
        TESTS_LOG.warn(context , t , messageFormat , args);
    }

    protected void debug(String messageFormat , Object...args) {
        TESTS_LOG.debug(context , messageFormat , args);
    }

    public void debug(Throwable t , String messageFormat , Object...args) {
        TESTS_LOG.debug(context , t , messageFormat , args);
    }
}

这是我的听众:

public class NeoTestListener implements ITestListener {
    private final File SCREENSHOTS_FOLDER = new File(EnvConf.getProperty("test_output.screenshots.folder"));
    private static final SimpleDateFormat FOLDER_NAME_FORMAT = new SimpleDateFormat("dd_MM_HH_mm_ss");
    private static final SimpleDateFormat LOG_NAME_FORMAT = new SimpleDateFormat("dd_MM_HH_mm_ss");
    private static final File TESTS_LOGS_FOLDER = new File(EnvConf.getProperty("test_output.logs.folder"));

    static {
        if (!TESTS_LOGS_FOLDER.exists()) {
            FileUtil.createFolder(TESTS_LOGS_FOLDER, true);
        }
    }

    private static String getTestMethodName(ITestResult iTestResult){
        return iTestResult.getMethod().getConstructorOrMethod().getName();
    }

    @Attachment
    public byte[] saveFailureScreenShot(DriverWrapper driver){
        return driver.getScreenshotAsByte();
    }

    @Attachment(value = "{0}", type = "text/plain")
    public static String saveTextLog(String message){
        return message;
    }

    @Override
    public void onStart(ITestContext context) {
        context.setAttribute("WebDriver", getDriver());

    }

    @Override
    public void onFinish(ITestContext context) {
    }

    @Override
    public void onTestStart(ITestResult result) {
        TESTS_LOG.info(result.getName() +" " +  result.getTestClass() );
//        TESTS_LOG.info("[Test: " + getTestClassName(result.getTestContext())+ " Started]");
    }

    @Override
    public void onTestSuccess(ITestResult result) {
    }

    @Override
    public void onTestFailure(ITestResult result) {
        TESTS_LOG.error(String.format("I am in onTestFailure method:=[%s] failed", getTestMethodName(result)));
        Object testClass = result.getInstance();
        DriverWrapper driver = getDriver();
        takeScreenshot(getTestMethodName(result));

        //Allure ScreenShot and SaveTestLog
        TESTS_LOG.info(String.format("Screenshot for class=[%s], method=[%s]", getTestClassName(result.getTestContext()), getTestMethodName(result)));
        saveFailureScreenShot(driver);
        try {
            saveLogTextFile(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        saveTextLog(getTestMethodName(result) + " failed and screenshot taken!");
    }

    @Attachment
    private byte[] saveLogTextFile(ITestResult result) throws IOException {
        return saveToLogFile(result.getTestContext());
    }

    @Override
    public void onTestSkipped(ITestResult result) {
    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
    }

    @Override
    public void onTestFailedWithTimeout(ITestResult result) {
    }

    private void takeScreenshot(String filePrefix){
        File dest = new File(SCREENSHOTS_FOLDER , filePrefix + "_" + FOLDER_NAME_FORMAT.format(new Date()) + ".png");
        takeScreenshot(dest, getDriver());
    }

    private void takeScreenshot(File destFile, DriverWrapper driver){
        File scrFile = driver.getScreenshotAsFile();
        Path src = Paths.get(scrFile.toURI());
        Path dest = Paths.get(destFile.toURI());
        try {
            Files.copy(src, dest , StandardCopyOption.REPLACE_EXISTING);
            TESTS_LOG.info("[[ATTACHMENT|" + destFile.getAbsolutePath() + "]]");
        } catch (IOException e) {
            TESTS_LOG.error("Failed to save screen shot at file: " + destFile.getName());
            TESTS_LOG.error(e.getMessage());
        }
    }

    private byte[] saveToLogFile(ITestContext context) throws IOException {
        File logFile = createLogFile(context);
        boolean created = FileUtil.createNewFile(logFile);

        if(created){
            List<String> testLogLines = TESTS_LOG.getAndDeleteLogsByTest(context.getName());
            if(testLogLines == null){
                TESTS_LOG.error(context, "test=[%s] don't have any log lines!" , context.getName());
            }else{
                FileUtil.writeToFile(logFile.getAbsolutePath(), testLogLines);
            }
        }else{
            TESTS_LOG.error(context, " failed to create test log file=[%s]", logFile.getAbsolutePath());
        }

        return FileUtils.readFileToByteArray(logFile);
    }

    private static File createLogFile(ITestContext context){
        return new File(TESTS_LOGS_FOLDER, String.format("%s_%s.log",getTestClassName(context), LOG_NAME_FORMAT.format(context.getStartDate())));
    }

    private static String getTestClassName(ITestContext context){
        return context.getAllTestMethods()[0].getInstance().getClass().getSimpleName();
    }

}

这是我的环境中的类测试示例:

public class ForgotPasswordTest extends BaseTest {
    private String verifyEmailURL = "";
    private static final String AUTOMATION_EMAIL= EnvConf.getProperty("automation.email.user");
    private static final String AUTOMATION_EMAIL_PASSWORD=EnvConf.getProperty("automation.email.password");
    private static final String AUTOMATION_WEBAPP_USER = "AUTOMATION_TESTER";
    private  ResetPasswordPage rpp;
    private final static String NEW_PASSWORD = "1Qaz2wsx3edc";
    private final static String ENVIRONMENT_BASE_URL = EnvConf.getProperty("base.url");

    @BeforeClass
    public void setup() {
        topBar = new TopBar(driver);
        rpp = new ResetPasswordPage(driver);
    }

    @Test(priority = 1)
    public void navigateToLoginPage(){
        loginPage.navigateAndVerify();
    }

    @Test(priority=2)
    public void sendChangePasswordInstructions() {
        ForgotPasswordPage fpp = loginPage.clickAndVerifyForgotPasswordButtonAction();
        fpp.sendForgotPasswordInstructions(AUTOMATION_EMAIL);
        Assert.assertTrue(loginPage.getForgotMsgAlertText().contains("Thank You, An Email Has Been Send"));
        info("Sending forget password instructions phase is successful!");
    }

    @Test(priority=3)
    public void verifyEmail(){
        verifyEmailURL = "";
        String regex = "href=\"([^\"]*)" ;
        String from = "<SOME_URL>";
        String subject = getSubject();
        String msg = MailHelper.getMessagesFromGmail( AUTOMATION_EMAIL, AUTOMATION_EMAIL_PASSWORD, from, subject, testStartTime);
        Pattern linkPattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher pageMatcher = linkPattern.matcher(msg);
        while (pageMatcher.find()) {
            if (pageMatcher.group(1).contains("reset-password")) {
                verifyEmailURL = pageMatcher.group(1);
                verifyEmailURL = ENVIRONMENT_BASE_URL.concat("/").concat(verifyEmailURL.split("/")[3]);
                info("Verify URL: " + verifyEmailURL);
            }
        }

        info("Verifying email address is successful!");

    }

    @Test(priority = 4)
    public void navigateToResetPasswordScreen(){
        navigateTo(verifyEmailURL);
    }

    @Test(priority=5)
    public void connectToUpdateNewPassword() {

        rpp.changeNewPassword(NEW_PASSWORD);
        info("Changing password is successful!");
    }

    @Test(priority=6)
    public void verifyPasswordChanged() {
        signIn(AUTOMATION_EMAIL, NEW_PASSWORD, true);
        assertTrue(topBar.verifyExistenceTopBarNavigationItem(TopRightNavBarItem.SUPPORT));
        info(String.format("Password for user=[%s] changed successfully", AUTOMATION_WEBAPP_USER));

    }

    private String getSubject(){
        return "Reset Password";
    }

}

每次我使用mvn clean test运行我的程序时,我都无法保存每个测试类的日志,它总是会保存上次运行的测试类和其他未按预期保存的日志文件。

缺少什么?我试图寻找解决方案,尝试了各种行动,但没有得到预期的结果。

共有1个答案

闻人树
2023-03-14

Maven Surefire插件具有参数

>

println(): First test...
22:32:25.537 [main] INFO igb.so.so65465538.FirstTest - log.info():  First test...

igb。所以so65465538。第二个测试输出。txt

println(): Second test...
22:32:25.561 [main] INFO igb.so.so65465538.SecondTest - log.info():  Second test...

 类似资料:
  • 我正在构建一个网络项目,它使用Maven进行构建,并使用泽西进行RESTful API。我使用TestNG和泽西测试框架编写了单元测试,并在内存中运行它们,而不是在网络服务器上。 在我的测试中,我对MySQL数据库进行了JDBC调用,并得到了一些结果。当我使用TestNG for Eclipse插件从Eclipse内部运行时,测试运行良好。但是,当我尝试使用以下两个命令之一从Maven运行时: 或

  • 问题内容: 我想使用Jenkins运行用TestNG框架编写的Selenium测试。 在jenkins job(Freestyle project)中配置的命令: 注意:对于,给定目录路径 当我运行作业(选项)时,它被Selenium的create session活动打中并给出错误。 注意: 同样的命令运行 成功地 从运行时 的命令行 。 控制台输出的错误日志: (jenkins) 以下是测试课程

  • 我想使用Jenkins运行在TestNG框架中编写的Selenium测试。 jenkins作业(Freestyle项目)中配置的命令: 控制台输出的错误日志:(jenkins) 下面是测试类: 以下是: null

  • 我正在从Eclipse迁移到Intellij理念。当我在Intellij理念中运行测试时,我得到了图像中显示的错误,但在Eclipse中没有。测试在Eclipse中运行良好。请告诉我如何解决这个问题。 这就是我运行测试的方式。我得到的错误是:

  • 问题内容: 我们有一个weblogic批处理应用程序,它可以同时处理来自使用者的多个请求。我们使用log4j记录目的。现在,我们登录到单个日志文件以处理多个请求。调试给定请求的问题变得很麻烦,因为所有请求都将日志记录在一个文件中。 因此,计划是每个请求只有一个日志文件。使用者发送一个请求ID,必须对其进行处理。现在,实际上可能有多个使用者将请求ID发送到我们的应用程序。因此,问题是如何根据请求隔离