All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.cucumber.junit.JustTestLahRunner Maven / Gradle / Ivy

Go to download

JustTestLah! is a JAVA test framework targeting projects that support multiple platforms, in particular Web, Android and iOS. It follows a BDD approach and allows testing against all platforms using the same feature files. JustTestLah's main aim is to make the configuration and the actual test code as easy as possible.

There is a newer version: 1.9-RC4
Show newest version
package io.cucumber.junit;

import static java.util.stream.Collectors.toList;

import io.cucumber.core.eventbus.EventBus;
import io.cucumber.core.feature.FeatureParser;
import io.cucumber.core.filter.Filters;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.core.options.CucumberOptionsAnnotationParser;
import io.cucumber.core.options.CucumberProperties;
import io.cucumber.core.options.CucumberPropertiesParser;
import io.cucumber.core.options.RuntimeOptions;
import io.cucumber.core.plugin.PluginFactory;
import io.cucumber.core.plugin.Plugins;
import io.cucumber.core.resource.ClassLoaders;
import io.cucumber.core.runtime.BackendServiceLoader;
import io.cucumber.core.runtime.BackendSupplier;
import io.cucumber.core.runtime.FeaturePathFeatureSupplier;
import io.cucumber.core.runtime.ObjectFactoryServiceLoader;
import io.cucumber.core.runtime.ObjectFactorySupplier;
import io.cucumber.core.runtime.ScanningTypeRegistryConfigurerSupplier;
import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier;
import io.cucumber.core.runtime.ThreadLocalRunnerSupplier;
import io.cucumber.core.runtime.TimeServiceEventBus;
import io.cucumber.core.runtime.TypeRegistryConfigurerSupplier;
import io.cucumber.plugin.event.TestRunFinished;
import io.cucumber.plugin.event.TestRunStarted;
import io.cucumber.plugin.event.TestSourceRead;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.time.Clock;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerScheduler;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;
import qa.justtestlah.configuration.CucumberOptionsBuilder;
import qa.justtestlah.configuration.Platform;
import qa.justtestlah.configuration.PropertiesHolder;

/**
 * Custom JUnit runner to dynamically set Cucumber options. Based on {@link
 * io.cucumber.junit.Cucumber}.
 */
public class JustTestLahRunner extends ParentRunner> {

  private static final String CLOUDPROVIDER_AWS = "aws";
  private static final String CLOUDPROVIDER_LOCAL = "local";
  public static final String AWS_JUNIT_GROUP_DESCRIPTION = "Test results";
  public static final String AWS_JUNIT_SUITE_DESCRIPTION = "AWS Devicefarm execution";

  private static final Logger LOG = LoggerFactory.getLogger(JustTestLahRunner.class);

  private List> children = new ArrayList<>();
  private List features = new ArrayList<>();
  private Plugins plugins = null;
  private EventBus bus = null;
  private PropertiesHolder properties = new PropertiesHolder();
  private boolean multiThreadingAssumed = false;

  private static final String CLOUD_PROVIDER = "cloudprovider";
  private static final String PLATFORM_KEY = "platform";
  private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";

  private Runner awsRunner;

  /**
   * Constructs a new {@link JustTestLahRunner}.
   *
   * @param clazz test class
   * @throws InitializationError {@link InitializationError}
   * @throws IOException {@link IOException}
   */
  public JustTestLahRunner(Class clazz) throws InitializationError, IOException {
    super(clazz);

    // Initialize Spring profiles and settings
    init();

    // Bridge logging to SLF4J
    bridgeLogging();

    if (properties.getProperty(CLOUD_PROVIDER, CLOUDPROVIDER_LOCAL).equals(CLOUDPROVIDER_AWS)) {
      LOG.info("Using qa.justtestlah.awsdevicefarm.AWSTestRunner");
      awsRunner = getAWSRunner(clazz);
    } else {
      CucumberOptionsBuilder.setCucumberOptions(properties);
      initCucumber(clazz);
    }
  }

  /**
   * This is the code taken from {@link io.cucumber.junit.Cucumber}
   *
   * @param clazz {@link Class}
   * @throws InitializationError {@link InitializationError}
   */
  private void initCucumber(Class clazz) throws InitializationError {
    Assertions.assertNoCucumberAnnotatedMethods(clazz);

    // Parse the options early to provide fast feedback about invalid options
    RuntimeOptions propertiesFileOptions =
        new CucumberPropertiesParser().parse(CucumberProperties.fromPropertiesFile()).build();

    RuntimeOptions annotationOptions =
        new CucumberOptionsAnnotationParser()
            .withOptionsProvider(new JUnitCucumberOptionsProvider())
            .parse(clazz)
            .build(propertiesFileOptions);

    RuntimeOptions environmentOptions =
        new CucumberPropertiesParser()
            .parse(CucumberProperties.fromEnvironment())
            .build(annotationOptions);

    RuntimeOptions runtimeOptions =
        new CucumberPropertiesParser()
            .parse(CucumberProperties.fromSystemProperties())
            .addDefaultSummaryPrinterIfAbsent()
            .build(environmentOptions);

    if (!runtimeOptions.isStrict()) {
      LOG.warn(
          "By default Cucumber is running in --non-strict mode.\n"
              + "This default will change to --strict and --non-strict will be removed.\n"
              + "You can use --strict or @CucumberOptions(strict = true) to suppress this warning");
    }

    // Next parse the junit options
    JUnitOptions junitPropertiesFileOptions =
        new JUnitOptionsParser().parse(CucumberProperties.fromPropertiesFile()).build();

    JUnitOptions junitAnnotationOptions =
        new JUnitOptionsParser().parse(clazz).build(junitPropertiesFileOptions);

    JUnitOptions junitEnvironmentOptions =
        new JUnitOptionsParser()
            .parse(CucumberProperties.fromEnvironment())
            .build(junitAnnotationOptions);

    JUnitOptions junitOptions =
        new JUnitOptionsParser()
            .parse(CucumberProperties.fromSystemProperties())
            .setStrict(runtimeOptions.isStrict())
            .build(junitEnvironmentOptions);

    this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID);

    // Parse the features early. Don't proceed when there are lexer errors
    FeatureParser parser = new FeatureParser(bus::generateId);
    Supplier classLoader = ClassLoaders::getDefaultClassLoader;
    FeaturePathFeatureSupplier featureSupplier =
        new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser);
    this.features = featureSupplier.get();

    // Create plugins after feature parsing to avoid the creation of empty files on
    // lexer errors.
    this.plugins = new Plugins(new PluginFactory(), runtimeOptions);

    ObjectFactoryServiceLoader objectFactoryServiceLoader =
        new ObjectFactoryServiceLoader(runtimeOptions);
    ObjectFactorySupplier objectFactorySupplier =
        new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader);
    BackendSupplier backendSupplier =
        new BackendServiceLoader(clazz::getClassLoader, objectFactorySupplier);
    TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier =
        new ScanningTypeRegistryConfigurerSupplier(classLoader, runtimeOptions);
    ThreadLocalRunnerSupplier runnerSupplier =
        new ThreadLocalRunnerSupplier(
            runtimeOptions,
            bus,
            backendSupplier,
            objectFactorySupplier,
            typeRegistryConfigurerSupplier);
    Predicate filters = new Filters(runtimeOptions);
    this.children =
        features.stream()
            .map(feature -> FeatureRunner.create(feature, filters, runnerSupplier, junitOptions))
            .filter(runner -> !runner.isEmpty())
            .collect(toList());

    LOG.info(
        "Found {} feature(s) in {}: {}",
        features.size(),
        System.getProperty("cucumber.features"),
        features);
  }

  @Override
  protected List> getChildren() {
    return children;
  }

  @Override
  protected Description describeChild(ParentRunner child) {
    return child.getDescription();
  }

  @Override
  protected void runChild(ParentRunner child, RunNotifier notifier) {
    child.run(notifier);
  }

  @Override
  protected Statement childrenInvoker(RunNotifier notifier) {
    Statement runFeatures = super.childrenInvoker(notifier);
    return new RunCucumber(runFeatures);
  }

  @Override
  public void setScheduler(RunnerScheduler scheduler) {
    super.setScheduler(scheduler);
    multiThreadingAssumed = true;
  }

  class RunCucumber extends Statement {
    private final Statement runFeatures;

    RunCucumber(Statement runFeatures) {
      this.runFeatures = runFeatures;
    }

    @Override
    public void evaluate() throws Throwable {
      if (multiThreadingAssumed) {
        plugins.setSerialEventBusOnEventListenerPlugins(bus);
      } else {
        plugins.setEventBusOnEventListenerPlugins(bus);
      }

      bus.send(new TestRunStarted(bus.getInstant()));
      for (Feature feature : features) {
        bus.send(new TestSourceRead(bus.getInstant(), feature.getUri(), feature.getSource()));
      }
      runFeatures.evaluate();
      bus.send(new TestRunFinished(bus.getInstant()));
    }
  }

  private void bridgeLogging() {
    SLF4JBridgeHandler.removeHandlersForRootLogger();
    SLF4JBridgeHandler.install();
  }

  private void init() {
    // set the active Spring profile to the current platform
    String platform = properties.getProperty(PLATFORM_KEY);
    if (platform == null || platform.isEmpty()) {
      LOG.info("No platform specified. Using default ({})", Platform.DEFAULT);
      platform = Platform.DEFAULT.getPlatformName();
      System.setProperty(PLATFORM_KEY, platform);
    }
    String springProfiles = System.getProperty(SPRING_PROFILES_ACTIVE);
    if (springProfiles != null && !springProfiles.isEmpty()) {
      springProfiles += "," + platform;
    } else {
      springProfiles = platform;
    }
    LOG.info("Setting platform to {}", platform);
    System.setProperty(SPRING_PROFILES_ACTIVE, springProfiles);
  }

  @Override
  public Description getDescription() {
    if (properties.getProperty(CLOUD_PROVIDER, CLOUDPROVIDER_LOCAL).equals(CLOUDPROVIDER_AWS)) {
      Description suiteDescription =
          Description.createSuiteDescription(AWS_JUNIT_SUITE_DESCRIPTION);
      suiteDescription.addChild(
          Description.createTestDescription("groupName", AWS_JUNIT_GROUP_DESCRIPTION));
      return suiteDescription;
    } else {
      return super.getDescription();
    }
  }

  /** this method uses reflection to avoid a compile-time dependency on justtestlah-awsdevicefarm */
  private Runner getAWSRunner(Class clazz) {
    try {
      return (Runner)
          Class.forName("qa.justtestlah.awsdevicefarm.AWSTestRunner")
              .getConstructor(Class.class)
              .newInstance(clazz);
    } catch (InstantiationException
        | IllegalAccessException
        | IllegalArgumentException
        | InvocationTargetException
        | NoSuchMethodException
        | SecurityException
        | ClassNotFoundException exception) {
      LOG.error(
          "Unable to create an instance of qa.justtestlah.awsdevicefarm.AWSTestRunner. Ensure justtestlah-aws is on your classpath (check your Maven pom.xml).",
          exception);
    }
    return null;
  }

  @Override
  public void run(RunNotifier notifier) {
    if (properties.getProperty(CLOUD_PROVIDER, CLOUDPROVIDER_LOCAL).equals(CLOUDPROVIDER_AWS)) {
      awsRunner.run(notifier);
    } else {
      super.run(notifier);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy