prashant-ramcharan / courgette-jvm

Multiprocess | Parallel Cucumber-JVM | Parallelize your Java Cucumber tests on a feature level or on a scenario level.
MIT License
132 stars 38 forks source link

Singleton created before Courgette runner nulled #58

Closed sgupta304 closed 6 years ago

sgupta304 commented 6 years ago

Hi Prashant,

I ran into an issue over the weekend. I created a singleton class which will in charge of gather all of the configurations needed during runtime and set it to a Suite object which the entire application should be able to use through out. However, once the Testng Courgette runner kicks off the suite singleton object becomes null.

Here is the singleton:

public class Suite {
  private static Suite ourInstance = new Suite();

  private Suite() {
    configFile = new ConfigFileReader(Resources.getFilePath(Resources.CONFIG_FILE));
  }

  public static synchronized Suite getInstance() {
    if (ourInstance == null) {
      synchronized (Suite.class) {
        if (ourInstance == null) {
          ourInstance = new Suite();
        }
      }
    }
    return ourInstance;
  }
}

Here is the courgette runner:

@Test
@CourgetteOptions(
  threads = 10,
  runLevel = CourgetteRunLevel.SCENARIO,
  rerunFailedScenarios = true,
  showTestOutput = true,
  reportTargetDir = "build",
  cucumberOptions =
      @CucumberOptions(
        features = "src/test/resources/Tmpf",
        glue = "Application.StepDefinitions",
        tags = {"@healthCheck"},
        plugin = {
          "progress",
          "json:build/cucumber-report/cucumber.json",
          "html:build/cucumber-report/cucumber.html"
        },
        strict = true
      )
)
public class TestSuiteRunner extends TestNGCourgette {
  private static Suite suite;
  public static Suite getSuite() { return suite; }
  private Log logger = new Log(TestSuiteRunner.class);

  @BeforeSuite(alwaysRun = true)
  public void initializeSuite() {
    Suite suite = Suite.getInstance();
    logger.info("Initialize Suite");
    suite.setSuiteProperties();
  }

  @AfterSuite
  public void afterSuite() {
    logger.info("End Suite");
  }
}

Is there anything that I am missing? I want to be able to initialize a few things before the threads are kicked off i.e. setup configurations for the entire suite, start appium server, possibly the BMP proxy server.

prashant-ramcharan commented 6 years ago

Singletons cannot be shared across multiple JVMs. Singletons are created on the Java heap and only available to the JVM in which it was created.

When you use Courgette, each test will start in a new JVM so any objects created during the test run will only exist in that one instance, this is why your singleton object is null for each test.

Some info here as well: https://stackoverflow.com/questions/12032895/how-to-create-singleton-java-class-for-multiple-jvm-support

I want to be able to initialize a few things before the threads are kicked off i.e. setup configurations for the entire suite, start appium server, possibly the BMP proxy server.

This setup will need to be moved outside your test and happen before your test run. This should not be as part of the actual test!

One way to do this is by using a Gradle task and running that task before running your tests.

sgupta304 commented 6 years ago

Ah I got it. That's was my hunch as well. Thanks for confirming it. I will keep that in mind.

When you say the setup needs to be done outside of the test. You are saying that anything that happens inside the BeforeSuite and AfterSuite hook needs to happen outside of the Courgette Runner class?

prashant-ramcharan commented 6 years ago

If you need to share a single instance of something like an appium server then this should be started before your test runs and outside the Courgette Runner class.

prashant-ramcharan commented 6 years ago

You could use the BeforeClass annotation to do "something" once.

The message "I will start my server once!" will only be called once before your tests start.

You could do something similar if you want to start a single server instance for example.

public class ChromeTestSuite extends TestNGCourgette {

    @BeforeClass
    public void beforeTestSuite() {
        System.out.println("I will start my server once!");
    }
}
sgupta304 commented 6 years ago

Sounds good. Thank you so much!