ch.inftec.ju.ee.webtest.DriverRule Maven / Gradle / Ivy
Show all versions of ju-testing-ee Show documentation
package ch.inftec.ju.ee.webtest;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.inftec.ju.testing.db.DbTestAnnotationHandler;
import ch.inftec.ju.testing.db.JuTestEnv;
import ch.inftec.ju.util.AssertUtil;
import ch.inftec.ju.util.JuRuntimeException;
import ch.inftec.ju.util.JuStringUtils;
import ch.inftec.ju.util.JuUrl;
import ch.inftec.ju.util.JuUtils;
import ch.inftec.ju.util.PropertyChain;
import ch.inftec.ju.util.ReflectUtils;
import ch.inftec.ju.util.ReflectUtils.AnnotationInfo;
import ch.inftec.ju.util.SystemPropertyTempSetter;
import ch.inftec.ju.util.TestUtils;
/**
* JUnit rule to run web tests with (multiple) selenium drivers.
* @author Martin
*
*/
public class DriverRule implements TestRule {
private static boolean copiedChromeDriverExe = false;
private static final String PROP_DRIVER = "ju-testing-ee.selenium.driver";
private static final String CHROME_DRIVER_EXE_PATH = "target/chromedriver.exe";
private static final Logger logger = LoggerFactory.getLogger(DriverRule.class);
private final WebTest testClass;
// JUnit will create a new Rule instance for every test method run. Therefore, we'll use static references
// to handle our drivers...
private static List driverHandlers = new ArrayList<>();
DriverRule(WebTest testClass) {
this.testClass = testClass;
}
@Override
public Statement apply(final Statement base, Description description) {
// Handle JuTestEnv annotations
Method method = TestUtils.getTestMethod(description);
List> testEnvAnnos = ReflectUtils.getAnnotationsWithInfo(method, JuTestEnv.class, false, true, true);
Collections.reverse(testEnvAnnos);
final SystemPropertyTempSetter tempSetter = DbTestAnnotationHandler.setTestEnvProperties(testEnvAnnos);
try {
PropertyChain pc = JuUtils.getJuPropertyChain();
if (DriverRule.driverHandlers.isEmpty()) {
// Get from the properties which drivers we should use to run the tests
String drivers[] = JuStringUtils.split(pc.get(PROP_DRIVER, true), ",", true);
Assert.assertTrue(String.format("No drivers specified in property %s", DriverRule.PROP_DRIVER), drivers.length > 0);
logger.debug("Initialize WebDrivers: " + Arrays.toString(drivers));
for (String driverType : drivers) {
logger.debug("Creating driver: " + driverType);
if ("HtmlUnit".equals(driverType)) {
DriverRule.driverHandlers.add(new HtmlUnitDriverHandler());
} else if ("Chrome".equals(driverType)) {
DriverRule.driverHandlers.add(new ChromeDriverHandler());
} else {
throw new JuRuntimeException(String.format("Unsupported selenium driver type: %s. Check value of property %s"
, driverType
, PROP_DRIVER));
}
}
}
return new Statement() {
public void evaluate() throws Throwable {
try {
// Run test case for with all drivers.
// We cannot use a for-iterator here as the closeAll method gets called when the for
// method tries to loop again, resulting in a ConcurrentModificationException.
for (int i = 0; i < driverHandlers.size(); i++) {
DriverHandler driverHandler = driverHandlers.get(0);
try (DriverHandler.DriverHandlerCreator driverCreator = driverHandler.newDriverHandlerCreator()) {
logger.info("Running test with WebDriver " + driverCreator);
testClass.driver = driverCreator.getDriver();
// If the evaluation fails, it will break our loop, i.e. if we want to run drivers
// d1 and d2 and in d1, we have an exception, d2 won't be executed at all...
base.evaluate();
}
}
} finally {
tempSetter.close();
}
}
};
} catch (Exception ex) {
tempSetter.close();
throw ex;
}
}
/**
* Disposes all open resources a DriverRule may hold.
*
* This should be called after the test class has been executed.
*/
public static void closeAll() {
// Clear DriverHandlers. This way, we may specify drivers to use in a system property.
driverHandlers.clear();
}
private static abstract class DriverHandler {
protected final PropertyChain pc = JuUtils.getJuPropertyChain();
public DriverHandlerCreator newDriverHandlerCreator() {
return new DriverHandlerCreator();
}
private class DriverHandlerCreator implements AutoCloseable {
private final WebDriver driver;
public DriverHandlerCreator() {
this.driver = createWebDriver();
}
public WebDriver getDriver() {
return driver;
}
@Override
public void close() throws Exception {
logger.debug("Closing WebDriver " + this.toString());
this.driver.quit();
}
@Override
public String toString() {
return String.format("%s [WebDriver: %s]", DriverHandler.this.getClass().getSimpleName(), this.driver);
}
}
protected abstract WebDriver createWebDriver();
}
public static class HtmlUnitDriverHandler extends DriverHandler {
@Override
protected WebDriver createWebDriver() {
boolean enableJavaScript = pc.get("ju-testing-ee.selenium.htmlUnit.enableJavascript", Boolean.class);
return new HtmlUnitDriver(enableJavaScript);
}
}
public static class ChromeDriverHandler extends DriverHandler {
@Override
protected WebDriver createWebDriver() {
System.setProperty("webdriver.chrome.driver", DriverRule.getChromeDriverExePath().toAbsolutePath().toString());
return new ChromeDriver();
}
}
private static Path getChromeDriverExePath() {
// We'll store the chromedriver in the target directory of the current working folder
Path chromeDriverExePath = Paths.get(DriverRule.CHROME_DRIVER_EXE_PATH);
try {
if (Files.notExists(chromeDriverExePath) || !DriverRule.copiedChromeDriverExe) {
URL chromedriverResourceUrl = JuUrl.resource("chromedriver.exe");
AssertUtil.assertNotNull("Couldn't find chromedriver.exe on classpath", chromedriverResourceUrl);
logger.debug("Copying chromedriver.exe from resource {} to {}"
, chromedriverResourceUrl
, chromeDriverExePath.toAbsolutePath());
try (InputStream is = new BufferedInputStream(chromedriverResourceUrl.openStream())) {
Files.copy(is, chromeDriverExePath, StandardCopyOption.REPLACE_EXISTING);
}
DriverRule.copiedChromeDriverExe = true;
}
} catch (Exception ex) {
throw new JuRuntimeException("Couldn't copy chromedriver.exe to " + chromeDriverExePath, ex);
}
return chromeDriverExePath;
}
}