Why does testng test fail when I have a class level variable in my test and run the test multi-threaded using data provider?

22 views
Skip to first unread message

prvn

unread,
Sep 13, 2016, 3:17:44 PM9/13/16
to testng-users
Please see my code below









@Test


public class MultiThreadTest {




        private String id;




        @Test(dataProvider = "dp")


        public void threadTest(String id) {


                System.out.println("BEFORE SETTING (THREAD: " + getCurrentThreadNumber() + ")------------------> " + id);


                this.id = id;


                setSleep();


                if (!id.equals(this.id)) {


                        Assert.fail("original: " + id + "  actual: " + this.id);


                }


        }




        private void setSleep() {

               int thread = getCurrentThreadNumber();

               if (thread % 2 == 0) {

                       try {

                               Thread.sleep(500);

                       } catch (InterruptedException e) {

                               e.printStackTrace();

                       }

               }

               System.out.println("AFTER SLEEP (THREAD: " + getCurrentThreadNumber() + ")------------------> " + id);

       }



        private int getCurrentThreadNumber() {


                String s = Thread.currentThread().getName().substring(12) + " ";


                int thread = Integer.parseInt(s.trim());


                return thread;


        }




        @DataProvider(name = "dp", parallel = true)


        public Object[][] iterativeSwagger(Method testMethod) {


                Object[][] data = null;


                int rowCount = 0;


                List<String> rowList = new ArrayList<>();


                String file = "./data.txt";


                try (Stream<String> inputStream = Files.lines(Paths.get(file))) {


                        rowList = inputStream.collect(Collectors.toList());


                        rowCount = rowList.size();


                        data = new Object[rowCount][1];


                        String columnValues;


                        for (int row = 0; row < rowList.size(); row++) {


                                columnValues = rowList.get(row);


                                data[row][0] = columnValues;


                        }


                } catch (IOException e) {


                        e.printStackTrace();


                }


                return data;


        }


}



In this code I'm doing nothing but reading a txt file which has a bunch of rows as below

75510277

 

75510300

NA

NA

75510211

75510277

75510218

75510277

75510277

75510214

75510255

75510266

75510277

NA

75510277

75510300

NA

NA

75510277

75510287

75510274

75510278

75510279

75510277

75510274

75510273

75510277

NA



Now when I run this test in multi threaded mode, a few tests fails because the class level variable "id" that I'm setting is different from the obtained one through data provider. The test perfectly works fine when I run using "parallel = false" . .. Also, If I run the same test without the class level variable "id" and make it a method level variable it works perfectly fine.


How do I achieve this? Please help.


Cédric Beust ♔

unread,
Sep 13, 2016, 3:42:45 PM9/13/16
to testng...@googlegroups.com

FYI, in your code, the id variable is not class level (static), it’s a regular instance field (not the answer to your question but a correction to your description of the problem).

Could you reformat your code to make it more readable?


-- 
Cédric


--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to testng-users+unsubscribe@googlegroups.com.
To post to this group, send email to testng...@googlegroups.com.
Visit this group at https://groups.google.com/group/testng-users.
For more options, visit https://groups.google.com/d/optout.

prvn

unread,
Sep 13, 2016, 3:48:35 PM9/13/16
to testng-users, ced...@beust.com
Sure. Thanks. I actually got mixed up. Sorry about that. 

I couldn't edit my OP. So I'm formatting it below


-- 
Cédric


To unsubscribe from this group and stop receiving emails from it, send an email to testng-users...@googlegroups.com.

prvn

unread,
Sep 13, 2016, 3:51:56 PM9/13/16
to testng-users
Additionally I also tried running testng.xml with parallel="classes" which I thought would create an object of my test class for every thread. But I still face the same issue.

Jeff

unread,
Sep 13, 2016, 5:21:29 PM9/13/16
to testng...@googlegroups.com
It appears that you assume there is a separate test class instance per thread.  When in fact you have a single test instance shared across multiple threads where the test method is being called in parallel on different threads.  When you call setSleep(), another thread runs 'threadTest(anotherId)' and overwrites this.id.

The way I have handled this in the past is create a map associating the variable you want to persist with the threadId such as:

public class Test {
     private Map<Long, String> idMap = new HashMap<>();

     ...

}

Then inside your test method(s) get or set the id based on the threadId:

@Test(dataProvider = "dp")
public void threadTest(String id) {
     long tId = Thread.currentThread().getId();

     idMap.put(tId, id);

     setSleep();

     if (!id.equals(idMap.get(tId)) {
        ...
     }
}

In your example, there is no benefit to storing id at the class instance level so I can only presume your actual use case is more complicated.

There is also a built-in mechanism in Java for doing ThreadLocal variables.  I haven't done it this way personally (ack!) so I don't have an example. See https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html for details.


- Jeff 


--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to testng-users+unsubscribe@googlegroups.com.

To post to this group, send email to testng...@googlegroups.com.
Visit this group at https://groups.google.com/group/testng-users.
For more options, visit https://groups.google.com/d/optout.



--
Jeff Vincent
See my LinkedIn profile at:
http://www.linkedin.com/in/rjeffreyvincent

prvn

unread,
Sep 13, 2016, 6:18:21 PM9/13/16
to testng-users
Code in my post is just an example. The test itself is much complicated. There is a need to have instance level variables in my test. I was able to successfully run the test with "ThreadLocal" earlier. But I always had a question as to why a new instance of the class is not created per thread. 
To unsubscribe from this group and stop receiving emails from it, send an email to testng-users...@googlegroups.com.

To post to this group, send email to testng...@googlegroups.com.
Visit this group at https://groups.google.com/group/testng-users.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages