SanchelliosProg
SanchelliosProg

Reputation: 2331

How take screenshot and attach it to Allure report, while using Cucumber and JUnit?

I'm using Cucumber, Selenium, Java, Maven and JUnit stack in my automation-test-project.

The goal is to take screenshots on fails and broken tests. I have found the solution for Java/Maven/JUnit stack:

@Rule
public TestWatcher screenshotOnFailure = new TestWatcher() {
    @Override
    protected void failed(Throwable e, Description description) {
        makeScreenshotOnFailure();
    }

    @Attachment("Screenshot on failure")
    public byte[] makeScreenshotOnFailure() {
        return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
    }
};

But, of course it does not work in case of using Cucumber, because it does not use any @Test methods.

So, I've decided to change @Rule to @ClassRule, to make it listen to any fails, so here it is:

@ClassRule
public static TestWatcher screenshotOnFailure = new TestWatcher() {
    @Override
    protected void failed(Throwable e, Description description) {
        makeScreenshotOnFailure();
    }

    @Attachment("Screenshot on failure")
    public byte[] makeScreenshotOnFailure() {
        logger.debug("Taking screenshot");
        return ((TakesScreenshot) Application.getInstance().getWebDriver()).getScreenshotAs(OutputType.BYTES);
    }
};

And this solution didn't help me.

So, the question is: "How to attach screenshots on fail, when I use Java/Selenium/Cucumber/JUnit/Maven in my test project?"

Upvotes: 1

Views: 19176

Answers (4)

user21073071
user21073071

Reputation: 1

public static void TakeScreenshot(string text)
        {
            byte[] content = ((ITakesScreenshot)driver).GetScreenshot().AsByteArray;
            AllureLifecycle.Instance.AddAttachment(text, "image/png", content);
        }
public static void Write(By by, string text)
        {
            try
            {
                driver.FindElement(by).SendKeys(text);
                TakeScreenshot( "Write : " + text);
            }
            catch (Exception ex)
            {
                TakeScreenshot( "Write Failed : " + ex.ToString());
                Assert.Fail();
            }
        }

Upvotes: 0

Chinthaka Devinda
Chinthaka Devinda

Reputation: 1085

This way you can attach screens to your allure report. Also use the Latest allure version in your pom file

import com.google.common.io.Files;
import io.qameta.allure.Attachment;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import java.io.File;
import java.io.IOException;

public class ScreenshotUtils {
    @Attachment(type = "image/png")
    public static byte[] screenshot(WebDriver driver)/* throws IOException */ {
        try {
            File screen = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            return Files.toByteArray(screen);
        } catch (IOException e) {
            return null;
        }
    }
}

Pom File :

 <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-cucumber4-jvm</artifactId>
        <version>${allure.version}</version>
    </dependency>

    <dependency>
        <groupId>io.qameta.allure</groupId>
        <artifactId>allure-junit4</artifactId>
        <version>${allure.version}</version>
        <scope>test</scope>
    </dependency>

Upvotes: 0

SanchelliosProg
SanchelliosProg

Reputation: 2331

The solution is just to add following code to your definition classes:

@After
public void embedScreenshot(Scenario scenario) {
    if (scenario.isFailed()) {
        try {
            byte[] screenshot = ((TakesScreenshot) Application.getInstance().getWebDriver())
                    .getScreenshotAs(OutputType.BYTES);
            scenario.embed(screenshot, "image/png");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Upvotes: 2

Amit Malik
Amit Malik

Reputation: 194

In the GlobalGlue

public class GlobalGlue {

  @Before
  public void before(Scenario scenario) throws Exception {
    CONTEXT.setScenario(scenario);
  }

  @After
  public void after() {
    WebDriverUtility.after(getDriver(), CONTEXT.getScenario());
  }

}

Create another class WebDriverUtility and in that add method:

public static void after(WebDriver driver, Scenario scenario) {
  getScreenshot(driver, scenario);
  driver.close();
}

and

public static void getScreenshot(WebDriver driver, Scenario scenario) {
if (scenario.isFailed()) {
  final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
  scenario.embed(screenshot, "image/png");
  log.info("Thread: " + Thread.currentThread().getId() + " :: "
      + "Screenshot taken and inserted in scenario report");
}

}

the main part is you need to embed the screen shot in scenario when scenario is failed:

 final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
 scenario.embed(screenshot, "image/png");

ExecutionContext.java

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import cucumber.api.Scenario;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openqa.selenium.WebDriver;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

/**
 * Maintains one webdriver per scenario and one scenario per thread.
 * Can be used for parallel execution.
 * Assumes that scenarios within one feature are not parallel.
 * Can be rewritten using <code>ThreadLocal</code>.
 *
 * @author dkhvatov
 */
public enum ExecutionContext {

  CONTEXT;

  private static final Logger log = LogManager.getLogger(ExecutionContext.class);

  private final LoadingCache<Scenario, WebDriver> webDrivers =
    CacheBuilder.newBuilder()
      .build(CacheLoader.from(scenario ->
        WebDriverUtility.createDriver()));

  private final Map<String, Scenario> scenarios = new ConcurrentHashMap<>();


  /**
   * Lazily gets a webdriver for the current scenario.
   *
   * @return webdriver
   */
  public WebDriver getDriver() {
    try {
      Scenario scenario = getScenario();
      if (scenario == null) {
        throw new IllegalStateException("Scenario is not set for context. " +
          "Please verify your glue settings. Either use GlobalGlue, or set " +
          "scenario manually: CONTEXT.setScenario(scenario)");
      }
      return webDrivers.get(scenario);
    } catch (ExecutionException e) {
      log.error("Unable to start webdriver", e);
      throw new RuntimeException(e);
    }
  }

  /**
   * Gets scenario for a current thread.
   *
   * @return scenario
   */
  public Scenario getScenario() {
    return scenarios.get(Thread.currentThread().getName());
  }

  /**
   * Sets current scenario. Overwrites current scenario in a map.
   *
   * @param scenario scenario
   */
  public void setScenario(Scenario scenario) {
    scenarios.put(Thread.currentThread().getName(), scenario);
  }

}

Upvotes: 0

Related Questions