Splitting and execution of JUnit test in multiple threads

Splitting and execution of JUnit test in multiple threads

Hello,

In this article, I will describe and present some example of how it is possible to run JUnit or TestNG test in multiple threads.
Let’s think about the situation that, that there is the test, which needs to validate 10 locales in 10 languages. Locales and languages are always dynamically taken from somewhere.
In this case, the most common way is:

for (Locale locale: locales) {
    for (Language language: languages) {
        assertStuff(locale, language);
    }
}

The problem here is that complexity will be always On^2.

So, what we can do with this?

We have multithreading! Bingo!

So, we can modify an example to something like:

for (Locale locale: locales) {
    Thread t = new Thread(() -> {
        for (Language language: languages) {
            assertStuff(locale, language);
        }
    }

    t.start();
}

Now it looks much better. But the problem is that we will not be seeing the error, because of assertions work in the main thread only.

Let’s modify the code a little bit and collect all the threads into the list:

List threads = new ArrayList<>();
for (Locale locale: locales) {
    Thread t = new Thread(() -> {
        for (Language language: languages) {
            assertStuff(locale, language);
        }
    }

    threads.add(t);
    t.start();
}

Alright, now we have the list of triggered threads. But what to with that?

After some researching about Java concurrency, I found the way how to implement Concurrent Assertions

    
void assertConcurrent(final String message, final List<? extends Runnable> runnables, final int maxTimeoutSeconds) throws InterruptedException {
        final int numThreads = runnables.size();
        final List exceptions = Collections.synchronizedList(new ArrayList<>());
        final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
        try {
            final CountDownLatch allThreadsReady = new CountDownLatch(numThreads);
            final CountDownLatch afterInitBlocker = new CountDownLatch(1);
            final CountDownLatch allThreadsAreDone = new CountDownLatch(numThreads);
            for (final Runnable submittedTestRunnable : runnables) {
                threadPool.submit(() -> {
                    allThreadsReady.countDown();
                    try {
                        afterInitBlocker.await();
                        submittedTestRunnable.run();
                    } catch (final Throwable e) {
                        exceptions.add(e);
                    } finally {
                        allThreadsAreDone.countDown();
                    }
                });
            }
            assertTrue("Timeout during threads initializing threads.", 
                    allThreadsReady.await(runnables.size() * WAIT_MULTIPLIER, TimeUnit.MILLISECONDS));
            
            afterInitBlocker.countDown();
            assertTrue(message +" timeout! More than" + maxTimeoutSeconds + "seconds", 
                    allThreadsAreDone.await(maxTimeoutSeconds, TimeUnit.SECONDS));
        } finally {
            threadPool.shutdownNow();
        }
        assertTrue(message + "failed with errors" + exceptions, exceptions.isEmpty());
}

And then, the final test could be like:

List threads = new ArrayList<>();
for (Locale locale: locales) {
    Thread t = new Thread(() -> {
        for (Language language: languages) {
            assertStuff(locale, language);
        }
    }

    threads.add(t);
    t.start();
}
assertConcurrent("Failures are found", threads, 120); // 120 is max timeout of the test

The explanation of the Concurrent assertion is not difficult if You have an experience with Multithreading.
But even if not – feel free to use this code

Happy testing!

Appium for the beginners. How to start with mobile automation?

Appium for the beginners. How to start with mobile automation?

Hello friends,

I work as a software test engineer already more than 6 years. And during these 5 years, I understood one thing – if You want to start to learn something new from the beginning, then You will Google for it. And as a result – You will find a lot of information without examples and simple explanation or You will find nothing at all, except some raw documentation.

I found that topics about mobile automation sometimes scare the people. So, I want to reveal the myths about mobile automation.

This article describes the simplest way of how to setup and run Your first test. An example is based on Appium 1.5.3 and Android system. It’s not about advanced usage of the latest version, etc.

What You need for starting with mobile automation

There are few thing that You need to have.

  1. Computer. MacBook is preferable to perform automation testing for iOS also.
  2. Pre-installed Android SDK or Android Studio is even better. Download here.
  3. Configured environment like for Selenium WEB testing.
  4. Appium desktop application latest version (1.5.3). Download here.

Base configuration

After downloading of Android Studio and setup of Android SDK You need to add the path to the folder with android SDK to the PATH of environments.

Run 1st test

  1. Create simple maven project in Eclipse or IntelliJ
  2. Add Appium dependency:
    <dependency>
        <groupId>io.appium</groupId>
        <artifactId>java-client</artifactId>
        <version>2.2.0</version>
    </dependency>
  3. Download test application that is used as an example. You can find it here or on Play Store here
  4. Create test class based on JUnit test framework and past the code inside:
    public class PresentationTest {
    
        private static final String MYREACTIONS_ID = "com.denyszaiats.myreactions:id/";
        protected static DesiredCapabilities capabilities;
    
        @Test
        public void testPresentationTapGame() throws InterruptedException, MalformedURLException {
            capabilities = new DesiredCapabilities();
            capabilities.setCapability("platformName", "Android");
            capabilities.setCapability("platformVersion", "6.0");
            capabilities.setCapability("deviceName", "emulator-5554");
            capabilities.setCapability("app", {PATH_TO_APK_FILE});
            AppiumDriver driver = new AndroidDriver(
                    new URL("http://0.0.0.0:4723/wd/hub"),
                    capabilities
            );
            driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
            driver.findElementById(MYREACTIONS_ID + "imageLangEn").click();
            driver.findElementById(MYREACTIONS_ID + "skip_login_button").click();
            driver.findElementById(MYREACTIONS_ID + "buttonGuideOk").click();
            driver.findElementById("android:id/home").click();
            driver.findElementById("android:id/home").click();
            driver.findElementByXPath("//*[@text='Crazy Fingers']").click();
            driver.findElementById(MYREACTIONS_ID + "buttonGuideOk").click();
            driver.findElementById(MYREACTIONS_ID + "handButton").click();
            driver.findElementById(MYREACTIONS_ID + "imageRightHand").click();
            driver.findElementById(MYREACTIONS_ID + "fingerButton").click();
            driver.findElementById(MYREACTIONS_ID + "imageIndexFinger").click();
            driver.findElementById(MYREACTIONS_ID + "startButton").click();
            WebElement tapButton = driver.findElementById(MYREACTIONS_ID + "imageTapButton");
            int x = tapButton.getLocation().x + 50;
            int y = tapButton.getLocation().y + 50;
            for (int i = 0; i < 350; i++) {
                driver.tap(1, x, y, 1);
            }
            String result = driver.findElementById(MYREACTIONS_ID + "resultsFasterClicker").getText();
            assertEquals(result, "350");
        }
    }
  5. Open Android simulator. Or create a new Android simulator. Or connect  Your Android test device. Do not forget to enable developer mode on Your real device.
  6. Open Appium desktop app and start it. Wait few seconds before Appium is ready to use. Run the test with JUnit.

 

This is it. Just several simple steps to run the simplest scenario with Appium.

I hope that now You’re confident with mobile automation and You know how to start with it.

Thanks for reading this article!

Sikuppium – how to combine Appium and Sikuli

Sikuppium – how to combine Appium and Sikuli

Sikuppium

Open source project which allows to mix Appium and Sikuli under one framework and easy manipulate by content and recognition of images. This is just a very nice helper in the automation testing to help test something that was left for manual visual testing. Now You can check without any problem in Your mobile automation project that correct Logo appears or correct photo is showed etc.

How to run

To run the sample test need to have pre-installed Maven 3 and Java 1.8 and need to execute next command from command line (example):

  • PLATFORM_NAME=”android” APPIUM_VERSION=”1.4.13″ NAME=”MyReaction” PLATFORM_VERSION=”4.4″ DEVICE=”Nexus 5″ APP=”/Developing/Android/SignedApp/MyReaction.apk” mvn clean install

Please, specify correct path to the application which You can find in the folder “app” under the main parent folder of the project.

Current example is adapted for the devices with resolution of display: 768×1280, 1080×1920, 1200×1920.

How to take a correct images

To take a correct screenshots need to use a tool SikuliX. You cannot take a screenshot directly from simulator screen. Only crop from the screenshot provided by WebDriver API in class DriverScreen.java (line 22)

Help and contact

Also You can find the application My Reaction on Play Store https://play.google.com/store/apps/details?id=com.denyszaiats.myreactions

Link to GitHub project – http://dzaiats.github.io/appium-sikuli-driver/

Smart text finder

This small but smart algorithm allows You to perform intelligent search for the words and phrases in the text. Imagine that You have recognised the text from the image. Usually there are many corrupted characters and it’s impossible to find something directly.

For example, You need to find phrase “Try Again”, but You have recognised text like ” gh try gh 6%^7 hjgasd 7^& dfg!44d T@y GaIN#%hjk in”. People are able to find something similar to “Try Again”. It’s “T@y GaIN”. But machine cannot find it. So, this algorithm allows to find such phrases.

The source code is here on GitHub

Here is algorithm:

public static boolean textIsFound(String pattern, String text) {
    pattern = pattern.toLowerCase();
    text = text.toLowerCase();

    if (text.contains(pattern)) {
        return true;
    } else if (text.replace(" ", "").contains(pattern.replace(" ", ""))) {
        return true;
    } else {
        String[] patternArr = pattern.split("\\W|\\s");
        String[] textArr = text.split("\\W|\\s");

        int matchTime = 0;
        int positionPrev = 0;
        for (String sp : patternArr) {
            for (String st : textArr) {
                if (st.equals(sp)) {
                    int positionNext = Arrays.asList(textArr).indexOf(st);
                    if (positionNext > positionPrev) {
                        positionPrev = positionNext;
                        matchTime++;
                        break;
                    }
                } else if (st.contains(sp)) {
                    matchTime++;
                    break;
                }
            }
        }

        boolean found = false;
        if (matchTime != patternArr.length) {
            String textJoined = String.join("", textArr);
            String patternJoined = String.join("", patternArr);
            String[] patternJoinedArr = patternJoined.split("");

            int position = 0;
            Map<Integer, String> positions = new TreeMap<Integer, String>();

            for (String s : patternJoinedArr) {
                while (position >= 0) {
                    position = textJoined.indexOf(s, position + 1);
                    if (position > -1) {
                        positions.put(position, s);
                    }
                }
                position = 0;
            }

            int count = 0;
            int countInLine = 1;

            for (Map.Entry entry : positions.entrySet()) {
                Integer i = (Integer) entry.getKey();
                if (positions.get(i + 1) != null && count < positions.size()) {
                    countInLine++;
                } else {
                    countInLine = 1;
                }
                if (countInLine >= patternJoined.length() * (1 - DERIVATION)) {
                    found = true;
                    break;
                }
                count++;
            }
        }

        return matchTime == patternArr.length || found;
    }
}

Enjoy it!

What to use: ArrayList vs LinkedList

I think that this article should be interesting for all who have a question “what data structure to use?”. In this article we will compare  ArrayList and LinkedList. Data type is Integer. List size is 1 000 000 items. We will perform a 10 000 operations with the lists, such as: adding to the front, adding to end, adding to the middle, remove from the head, remove from the end, remove from the middle.
How we will do it:

public void listTimeTest(){
    List<Integer> list = new ArrayList<Integer>();
    //List<Integer> list = new LinkedList<Integer>();

    for(int i = 0; i < 1000000; i++){
        list.add(i);
    }

    long start = System.currentTimeMillis();

    for (int i = 0; i < 10000; i++){
        //list.add(0, i); To the front
        //list.add(i); To the end
        //list.add(list.size()/2, i); Into the middle

        //list.remove(0); From the head
        //list.remove(list.size()-1); From the end
        //list.remove(list.size()/2); From the middle
    }

    long end = System.currentTimeMillis();

    System.out.print("Time of execution is: " + (end - start) + "ms.");
}

Time results are very interesting:

  1. Add to the front:
    • LinkedList – Time of execution is: 5ms.
    • ArrayList – Time of execution is: 4358ms.
  2. Add to the end:
    • LinkedList – Time of execution is: 2ms.
    • ArrayList – Time of execution is: 1ms.
  3. Add into the middle:
    • LinkedList – Time of execution is: 26329ms.
    • ArrayList – Time of execution is: 1703ms.
  4. Remove from the head:
    • LinkedList – Time of execution is: 4ms.
    • ArrayList – Time of execution is: 4220ms.
  5. Remove from the end:
    • LinkedList – Time of execution is: 3ms.
    • ArrayList – Time of execution is: 1ms.
  6. Remove from the middle:
    • LinkedList – Time of execution is: 27737ms.
    • ArrayList – Time of execution is: 1793ms.

So, as You see, ArrayList won everything except operations in the head of the list.

Resolution:

  • If You want to perform operations in the front  of the list – use LinkedList
  • If You want to perform operations in the end  of the list – doesn’t matter what to use because time of execution is almost the same (approx. 1-3ms)
  • If You want to perform operations in the middle of the list –  use only ArrayList. Difference in the time is almost 15x times faster then LinkedList.

Thanks for reading my article.

Cheers.

 

“Specification by Example” in example. Java, Selenium, Concordion, Junit, Maven.

Hello!
In this article I will try to explain, how to use approach “Specification by Example” on real project. What tool and framework You need for this. This example will include part of real automation testing framework (ATF) for testing of Web UI application. ATF is based on Java, JUnit, Maven, Selenium and Concordion.

Ok, so what You need to know to start with thing like this? First of all it’s good to have some practice with Java, JUnit and automation Web UI testing (I mean Selenium). If You know everything of it and if You know what is Page Object pattern – let’s imagine, that You have ready ATF based on Java, Selenium and start with implementing of Concordion into this ATF. This examples shows a real example of creation new item on Atlassian’s Jira test project.

Let’s do everything step by step.

Step 1.

Add dependency in pom.xml

<dependency>
    <groupId>org.concordion</groupId>
    <artifactId>concordion</artifactId>
    <version>1.4.7</version>
</dependency>

Step 2.

Need to configure pom.xml correctly for using correct folders and making of correct output reports. Add next section to Your pom.xml file:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>add-specs-source</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>add-test-source</goal>
                    </goals>
                    <configuration>
                        <sources>
                            <source>${basedir}/src/test/specs</source>
                        </sources>
                    </configuration>
                </execution>
                <execution>
                    <id>add-test-resource</id>
                    <phase>generate-test-sources</phase>
                    <goals>
                        <goal>add-test-resource</goal>
                    </goals>
                    <configuration>
                        <resources>
                            <resource>
                                <directory>${basedir}/src/test/specs</directory>
                                <includes>
                                    <include>**/*.html</include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skip>true</skip>
                <systemProperties>
                    <property>
                        <name>concordion.output.dir</name>
                        <value>target/specs</value>
                    </property>
                </systemProperties>
            </configuration>
            <executions>
                <execution>
                    <id>behavior-test</id>
                    <phase>test</phase>
                    <goals>
                        <goal>test</goal>
                    </goals>
                    <configuration>
                        <skip>false</skip>
                        <testSourceDirectory>src/test/java</testSourceDirectory>
                        <includes>
                            <include>**/*Test.java</include>
                        </includes>
                        <excludes>
                            <exclude>**/*Fixture.java</exclude>
                        </excludes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Step 3.

From previous step You see, that out *.html example will be stored in /src/test/specs. So, create in Your project under /src/test folder specs. And create in this folder an empty HTML file. It will be our specification. Put next data into HTML file:

<html xmlns:concordion="http://www.concordion.org/2007/concordion">
    <head lang="en">
        <meta charset="UTF-8"/>
</head>
    <body>
        <h2>Test - create new issue and verify that it's created successfully</h2>
        <p>Given username</p>
         <span concordion:set="#username">denys.zaiats@gmail.com</span>
        <p>Given password</p>       
         <span concordion:set="#password">dzaiats2015</span>       
         <p concordion:execute="performLogin(#username, #password)">Step 1. Perform login</p>       
        <p>Summary: </p>
         <span concordion:set="#summary">New issue created by automated test</span>        
        <p>Description: </p>
         <span concordion:set="#description">Issue description</span>       
        <p>Priority: </p>
         <span concordion:set="#priority">Major</span>
         <p concordion:execute="createNewIssue(#summary, #description, #priority)">
            Step 2. Create new issue
         </p>     
         <p concordion:assertTrue="isIssueCreated()">Step 3. Assert - new issue is created</p>
    </body>
</html>

As You see – You need to use Concordion schema for using of concordion’s attributes. There are next most used concordion attributes which You can use:

  • concordion:assertEquals
  • concordion:assertTrue
  • concordion:set
  • concordion:execute
  • concordion:run
  • concordion:execute on a <table>
  • concordion:execute on a <list>
  • concordion:verifyRows

You can set parameters which You want to pass in methods. You can just execute method if You want to perform any void method. More information about using of this You can find here

Step 4.

Need to create correct java test with ConcordionRunner. Here is presented an example of it:

@RunWith(ConcordionRunner.class)
public class CreateNewIssueSuccessfullyTest extends TestBase{

    public void performLogin(String username, String password){
        LoginPage loginPage = home.loginPage();
        loginPage.login(username, password);
    }

    public void createNewIssue(String summary, String description, String priority){
        home.createNewIssue(summary, description, priority);
    }

    public boolean isIssueCreated(){
        return home.isNewIssueCreated();
    }
}

Step 5.

If You did everything correctly – You should be able to clean and install project without problem with using of Maven. After successful test run new report-file will appear in folder /target/specs/

What the benefits of using this approach? The answer is obviously. You have good readable living documentations which are the part of tests. And business logic is represented not only in the code, but also in readable view for non-technical stuff.

A bit later I will post a link to live example with source code of this implementation. If You have any question – just send me e-mail to itarray.net@gmail.com

Thanks for reading my articles.
Cheers.