3

So I'm attempting to run parallel parameterized tests. I have a solution where the same test can run in parallel with the parameters supplied for example say I have the following:

@Test
public void someTest1(){
}

@Test
public void someTest2(){
}

I can get someTest1() to run with all the parameters concurrently, but someTest2() will have still have to wait for someTest1() to complete with all parameters before executing. I was wondering if anyone knew of a solution to be able to run someTest1() with all parameters and someTest2() with all parameters concurrently? I've tried tempus-fugit concurrent test runner, which works great for tests that are not parameterized...

Below is code that I have for currently running each parameter test in parallel.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.runners.Parameterized;
import org.junit.runners.model.RunnerScheduler;

/**
 * Class used from the following source:
 * http://jankesterblog.blogspot.com/2011/10
 * /junit4-running-parallel-junit-classes.html
 * 
 * @author Jan Kester
 * 
 */
public class Parallelized extends Parameterized {

    private static class ThreadPoolScheduler implements RunnerScheduler {
        private ExecutorService executor;

        public ThreadPoolScheduler() {
            String threads = System.getProperty("junit.parallel.threads", "16");
            int numThreads = Integer.parseInt(threads);
            executor = Executors.newFixedThreadPool(numThreads);
        }

        public void finished() {
            executor.shutdown();
            try {
                executor.awaitTermination(12, TimeUnit.HOURS);
            } catch (InterruptedException exc) {
                throw new RuntimeException(exc);
            }
        }

        public void schedule(Runnable childStatement) {
            executor.submit(childStatement);
        }
    }

    /**
     * Instantiates a new parallelized.
     * 
     * @param klass
     *            the klass
     * @throws Throwable
     *             the throwable
     */
    public Parallelized(Class<?> klass) throws Throwable {
        super(klass);
        setScheduler(new ThreadPoolScheduler());
    }
}

The code below is an example test, BaseSuite doesn't contain anything of much importance. These are being used with selenium so it just sets the webDriver. The getAllButOpera() method returns a collection of browser types that contain Internet Explorer, Firefox, and Chrome. These parameters are used to run the same test on firefox, ie, and chrome concurrently. I would like to run the two tests in the class at the same time which is what I am having trouble with.

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Collection;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized.Parameters;
import org.openqa.selenium.WebDriver;


/**
 * The Class SampleSuite1.
 * 
 * @author Reid McPherson
 */
@RunWith(Parallelized.class)
public class SampleSuite1 {
    WebDriver driver;
    /**
     * Data.
     * 
     * @return the collection
     */
    @Parameters
    public static Collection<Object[]> data(){
          List<Object[]> browsers = new ArrayList<Object[]>();
    browsers.add(new String[]{"Firefox"});
    browsers.add(new String[]{"Chrome"});
    browsers.add(new String[]{"IE"});
    return browsers;
    }

    /**
     * Instantiates a new sample suite1.
     * 
     * @param type
     *            the type
     */
    public SampleSuite1(String type){
        switch (type) {
    case "FIREFOX":
        driver = new FirefoxDriver();
        break;
    case "IE":
        driver = new InternetExplorerDriver();
        break;
    case "CHROME":
        System.setProperty("webdriver.chrome.driver", PATHTOCHROMEEXE);
        driver = new ChromeDriver();
        break;
    case "OPERA":
        driver = new OperaDriver();
        break;
    default:
        throw new RuntimeException("Browser type unsupported");
    }
    // Set the timeout.
    driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
    }

    /**
     * Sets the up.
     */
    @Before
    public void setUp() {
        driver.get("http://www.google.com");
    }

    /**
     * Test navigation succeeded.
     */
    @Test
    @TestDescription("Navigation Test")
    public void navigationShouldSucceed() {
        String pageSource = driver.getPageSource();
        assertTrue(pageSource.contains("Google"));
    }

    /**
     * Test title.
     */
    @Test
    @TestDescription("This method tests the web page title.")
    public void titleShouldBeGoogle() {
        assertEquals(driver.getTitle(), "Google");
    }

    @After
    public void finished(){
    driver.close();
    }


}
Toby
  • 9,523
  • 8
  • 36
  • 59
Reid Mac
  • 2,411
  • 6
  • 37
  • 64
  • i'm not looking to qualify for an answer here, but this is not the proper way to test concurrent code. if you want to learn how to do this i may point you to the relevant section of "java concurrency in practice" book. – Lefteris Laskaridis Apr 13 '12 at 13:29
  • The code I am testing isn't concurrent, I just want to run the tests concurrently. – Reid Mac Apr 13 '12 at 13:31
  • well, perhaps i'm misinterpreting your intention here: i assume that the reason that you ask how to run your tests concurrently is because you need to test your code's concurrent behavior. – Lefteris Laskaridis Apr 13 '12 at 13:35
  • Then problem probably is not JUnit itself, but Webdriver. Look how to configure it to run in multiple instances. – Artem Oboturov Apr 13 '12 at 13:38
  • My goal is to test a website on different browsers without having to rewrite the test for each browser. Whether they occur at the same time or not is really not important to me at the moment. – Reid Mac Apr 13 '12 at 13:40
  • @Artem Oboturov I am using the Selenium Grid which is what is used to run multiple selenium tests on different browsers at the same time, so that is working fine. – Reid Mac Apr 13 '12 at 13:41
  • Can you clean your example from private code utilities.* used in SampleSuite1 class? – Artem Oboturov Apr 16 '12 at 13:43
  • I believe I have removed it all, let me know if there is something I missed. – Reid Mac Apr 16 '12 at 14:10
  • I'm the author of tempus-fugit and would be happy to incorporate something into the library to support this. Might be worth chating offline (especially if you've already solved it!). Cheers – Toby Apr 20 '12 at 09:59
  • That would be great. I thought I had the solution, but it's more of a hack than a solution. Essentially, it is not guaranteed that the same test will not run at the same time as another one is running. I've made it where there is a greater chance that the same test will not run at the same time. This will work for now, but it would be better to be guaranteed that the same test will not run at the same time. – Reid Mac Apr 23 '12 at 12:19

4 Answers4

5

As I told the question is in implementation of JUnit.

You can see that:

Parallelized extends Parametrized extends Suite extends ParentRunner

on the other hand:

ConcurrentTestRunner extends BlockJUnit4ClassRunner extends ParentRunner

hence they are from different hierarchies of inheritance.

And now what you have to look at is implementation of the:

org.junit.runners.ParentRunner#getChildren

method. For the org.junit.runners.BlockJUnit4ClassRunner it is:

protected List<FrameworkMethod> computeTestMethods() {
    return getTestClass().getAnnotatedMethods(Test.class);
}

which generates all methods with annotations. But for org.junit.runners.Parameterized it is:

for (int i= 0; i < parametersList.size(); i++)
  runners.add(newtestClassRunnerForParameters(getTestClass().getJavaClass(),
                parametersList, i));

And the last one gives only classes.

Proposal: override your Parallelized class with definition of org.junit.runners.ParentRunner#getChildren from BlockJUnit4ClassRunner.

Jens
  • 67,715
  • 15
  • 98
  • 113
Artem Oboturov
  • 4,344
  • 2
  • 30
  • 48
  • I'm a bit confused. So should I not extend `Parameterized` in the `Parallelized` class? How do I get a `List` to a `List` – Reid Mac Apr 16 '12 at 15:26
  • Yes. I saw it already. I tried to use a runner from BlockJUnit4ClassRunner but it's not compatible. What if you will use org.junit.experimental.ParallelComputer and org.junit.experimental.theories.ParametersSuppliedBy? ParallelComputer gives you parallel execution, so that you can drop Parallelized already. And ParametersSuppliedBy could be used to generate data as in public static Collection data() and then you can create a driver per test method. Override solution would be a really big hack and IMHO will be somewhat awkward. – Artem Oboturov Apr 16 '12 at 15:52
  • I have tried ParallelComputer, but if I use ParallelComputer the results are no longer reported through the framwork. – Reid Mac Apr 16 '12 at 16:00
  • Ok. I think there's a really simple be not elegant solution: you can create a test class per test method. – Artem Oboturov Apr 16 '12 at 16:06
  • Yes, that would work, but the way the tests have to be organized at my job does not make that a viable solution. Thanks for the help so far btw. – Reid Mac Apr 16 '12 at 16:12
  • In fact even in JUnit you can find a static class per test method approach. Look at org.junit.tests.experimental.rules.ExpectedExceptionTest from JUnit's own test harness. – Artem Oboturov Apr 27 '12 at 16:10
  • Did you try TestNG? Use it like written in [http://stackoverflow.com/questions/6902693/how-to-inject-parameter-into-constructor-of-testng-class](http://stackoverflow.com/questions/6902693/how-to-inject-parameter-into-constructor-of-testng-class). It supports parallel threads on tests. – Artem Oboturov Apr 29 '12 at 21:25
  • Yes, I would LOVE to use TestNG, but I was told not to because they do not want competing libraries with JUnit. >_ – Reid Mac Apr 30 '12 at 12:21
  • Fortunately for my project I do migration just now. Try to make them reconsider. I really loved @DataProvider annotation and parametrized @Test(dataProvider = ...). – Artem Oboturov Apr 30 '12 at 12:36
  • I did try, but I'm new here and I'm at the bottom of the Totem pole, so my influence is not great. I have something that will work for now. I will keep trying to pull for TestNG though. Thanks for the help. =) – Reid Mac Apr 30 '12 at 12:44
1

Thanks for the help, I ended up using code from here in addition to running concurrent suites and it gave me the ability to run the tests simultaneously, but it will not run the same test at the same time.

Community
  • 1
  • 1
Reid Mac
  • 2,411
  • 6
  • 37
  • 64
  • Never thought of switching to TestNG? – Franz Ebner Jun 12 '12 at 21:29
  • Yes I have. I have used TestNG, and I love it. I was told not to use TestNG since they didn't want to have competing libraries, and didn't want to re-write the tests to be compatible with TestNG (even though TestNG makes this quite easy to do). The decision wasn't up to me, but I definitely fought for it lol. – Reid Mac Jun 13 '12 at 14:24
0

I am also using the code from Jeeunit... however even the version 1.0 has a bug.

In the ConcurrentRunnerScheduler the finished method must be implemented.

So I just pulled the code and implemented it like the suiteFinished() method:

@Override
    public void finished() {
        try {
        while (!tasks.isEmpty())
            tasks.remove(completionService.take());
    }
    catch (InterruptedException e) {
        System.out.println("suite fin");
        Thread.currentThread().interrupt();
    }
    finally {
        while (!tasks.isEmpty())
            tasks.poll().cancel(true);
        executorService.shutdownNow();
    }

}
Laura Liparulo
  • 2,849
  • 26
  • 27
-1

This is what I have tried, and it works really well for me.

public class ParallelTests {
   static ExecutorService eService ;
   public static void main(String[] args) {
      testA() ;
      testB() ;
      testC() ;
   }
   public static void testA() {
      eService = Executors.newCachedThreadPool() ;
      for (int i = 0 ; i < 10 ; i++) {
         TestA testA = new TestA() ;
         eService.execute(testA) ;
      }
      eService.shutdown() ;
      while(!eService.isShutDown()) {
      }
   }
//same for testB and testC
}

public class TestA implements Runnable {
    public TestA() {
    }
    @Test
    public myTest throws Throwable {
    }
}
Siddharth
  • 9,349
  • 16
  • 86
  • 148