Lazy dataprovider with factory on class constructor

1,287 views
Skip to first unread message

Tamás Kende

unread,
Oct 17, 2011, 8:37:04 AM10/17/11
to testng...@googlegroups.com
Hi all,

maybe I misunderstand something, but the lazy dataprovider means that the test class initialization will happen shortly before the test run, isn't it? I have the following case:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;

public class LazyDataprovider {

String a;
String b;
String c;

@DataProvider(parallel = true)
public static Iterator<Object[]> settings(){
List<Object[]> ret = new ArrayList<Object[]>() {
{
add(new Object[]{"er","12","hwhr"});
add(new Object[]{"asd","234","wrhera"});
add(new Object[]{"ge","76","warer"});
add(new Object[]{"jztj","876","ertu"});
add(new Object[]{"ilgt","11232","zukz"});
add(new Object[]{"ewerr","6512","w56ua"});
}
};
return ret.iterator();
}
@Factory(dataProvider="settings")
public LazyDataprovider(String a, String b, String c){
this.a=a;
this.b=b;
this.c=c;
System.out.println("Initialize"+this.toString());
}
@BeforeClass
public void beforeClass(){
System.out.println("BeforeClass"+this.toString());
}
@Test
public void test(){
System.out.println("Test"+this.toString());
}

@Override
public String toString() {
return "LazyDataprovider [a=" + a + ", b=" + b + ", c=" + c + "]";
}
}

Is not it true, that with lazy data provider I should see something similar in the output:
InitializeLazyDataprovider [a=er, b=12, c=hwhr]
BeforeClassLazyDataprovider [a=er, b=12, c=hwhr]
TestLazyDataprovider [a=er, b=12, c=hwhr]
InitializeLazyDataprovider [a=asd, b=234, c=wrhera]
BeforeClassLazyDataprovider [a=asd, b=234, c=wrhera]
TestLazyDataprovider [a=asd, b=234, c=wrhera]
...

and not the current:
InitializeLazyDataprovider [a=er, b=12, c=hwhr]
InitializeLazyDataprovider [a=asd, b=234, c=wrhera]
InitializeLazyDataprovider [a=ge, b=76, c=warer]
InitializeLazyDataprovider [a=jztj, b=876, c=ertu]
InitializeLazyDataprovider [a=ilgt, b=11232, c=zukz]
InitializeLazyDataprovider [a=ewerr, b=6512, c=w56ua]
BeforeClassLazyDataprovider [a=er, b=12, c=hwhr]
TestLazyDataprovider [a=er, b=12, c=hwhr]
BeforeClassLazyDataprovider [a=asd, b=234, c=wrhera]
TestLazyDataprovider [a=asd, b=234, c=wrhera]
BeforeClassLazyDataprovider [a=ge, b=76, c=warer]
TestLazyDataprovider [a=ge, b=76, c=warer]
...

If the current is the expected behaviour, how can I avoid the initialization, because I have to run hundreds of tests, which will not be effective in this way. I'm using version 6.3.

Thank you,

Regards,

Tamas

Krishnan Mahadevan

unread,
Oct 17, 2011, 11:01:22 AM10/17/11
to testng...@googlegroups.com
For the lazy loading to really happen, you would need to implement the "Iterator" interface in your test class and define the object creation, everytime public Object next() is called on your iterator.

You then re-define your dataprovider annotated method to return an Iterator.

That is what the Next Generation Testing book says :)

Thanks & Regards
Krishnan Mahadevan

"All the desirable things in life are either illegal, expensive, fattening or in love with someone else!"



--
You received this message because you are subscribed to the Google Groups "testng-users" group.
To post to this group, send email to testng...@googlegroups.com.
To unsubscribe from this group, send email to testng-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/testng-users?hl=en.

Cédric Beust ♔

unread,
Oct 17, 2011, 12:33:44 PM10/17/11
to testng...@googlegroups.com
Like Krishnan explained, the lazy behavior means that TestNG will call next() on your iterator, run the test, then next(), then run the test, etc...

The problem with your code is that even though you are returning an iterator, you are creating all the objects upfront. If you return a "real" lazy iterator that only creates the object necessary before returning it in next(), you will see the expected behavior.

-- 
Cédric




--

Tamás Kende

unread,
Oct 18, 2011, 2:48:59 AM10/18/11
to testng-users
Thank you, I will try to implement my own iterator... meanwhile I
found another thing. If there are dependant methods in the test class,
the output will change to:

InitializeLazyDataprovider [a=er, b=12, c=hwhr]
InitializeLazyDataprovider [a=asd, b=234, c=wrhera]
...
BeforeClassLazyDataprovider [a=er, b=12, c=hwhr]
BeforeClassLazyDataprovider [a=asd, b=234, c=wrhera]
...
TestLazyDataprovider [a=er, b=12, c=hwhr]
TestLazyDataprovider [a=asd, b=234, c=wrhera]
...
Test2LazyDataprovider [a=er, b=12, c=hwhr]
Test2LazyDataprovider [a=asd, b=234, c=wrhera]
...
Test3LazyDataprovider [a=er, b=12, c=hwhr]
Test3LazyDataprovider [a=asd, b=234, c=wrhera]
Test3LazyDataprovider [a=ge, b=76, c=warer]
...

is this because the testNG have to create the test execution plan, and
cannot do this without triggering the before class in all test
instances? Or is this related to the preserve-order set up? Will it be
solved if I implement my own iterator?

Thank you for your answers,

Regards,

Tamas

Tamás Kende

unread,
Oct 18, 2011, 3:02:04 AM10/18/11
to testng-users
Dear all,

I tried to implement my iterator, based on the book, but I did
something wrong, can someone point me where?
My test class:

public class LazyDataprovider {
String a;
String b;
String c;

@DataProvider(parallel = true)
public static Iterator settings(){
return new SettingIterator();
}


@Factory(dataProvider="settings")
public LazyDataprovider(String a, String b, String c){
this.a=a;
this.b=b;
this.c=c;
System.out.println("Initialize"+this.toString());
}

@BeforeClass
public void beforeClass(){
System.out.println("BeforeClass"+this.toString());
}

@Test
public void test(){
System.out.println("Test"+this.toString());
}
/*
@Test(dependsOnMethods={"test"})
public void test2(){
System.out.println("Test2"+this.toString());
}

@Test(dependsOnMethods={"test2"})
public void test3(){
System.out.println("Test3"+this.toString());
}
*/
@Override
public String toString() {
return "LazyDataprovider [a=" + a + ", b=" + b + ", c=" + c + "]";
}
}

And my iterator:

public class SettingIterator implements Iterator {
private int index = 0;
List<Object[]> ret = new ArrayList<Object[]>() {
{
add(new Object[]{"er","12","hwhr"});
add(new Object[]{"asd","234","wrhera"});
add(new Object[]{"ge","76","warer"});
add(new Object[]{"jztj","876","ertu"});
add(new Object[]{"ilgt","11232","zukz"});
add(new Object[]{"ewerr","6512","w56ua"});
}
};

public boolean hasNext() {
return index<ret.size();
}

public Object next() {
return ret.get(index++);
}

public void remove() {
throw new UnsupportedOperationException("PLEASE DONT");
}
}

What did I wrong? I still receive the same output...

Thank you so much,

Tamas

Cédric Beust ♔

unread,
Oct 18, 2011, 3:06:01 AM10/18/11
to testng...@googlegroups.com
Hi Tamás,

I'm not sure how to interpret the output you're giving, but I'm guessing you are referring to the fact that when a method b() depends on a(), all the instances of a() need to have run before the first b() method will run.

You can change this behavior by setting <suite group-by-instances="true">.

If I misunderstood your question, can you rephrase with a simple example?

Thanks.

-- 
Cédric

Tamás Kende

unread,
Oct 18, 2011, 3:15:29 AM10/18/11
to testng-users
Ok it seems it works now... eclipse did not build my changes. Now the
output is:
InitializeLazyDataprovider [a=er, b=12, c=hwhr]
InitializeLazyDataprovider [a=asd, b=234, c=wrhera]
InitializeLazyDataprovider [a=ge, b=76, c=warer]
InitializeLazyDataprovider [a=jztj, b=876, c=ertu]
InitializeLazyDataprovider [a=ilgt, b=11232, c=zukz]
InitializeLazyDataprovider [a=ewerr, b=6512, c=w56ua]
BeforeClassLazyDataprovider [a=er, b=12, c=hwhr]
TestLazyDataprovider [a=er, b=12, c=hwhr]
BeforeClassLazyDataprovider [a=asd, b=234, c=wrhera]
TestLazyDataprovider [a=asd, b=234, c=wrhera]

can't we reach somehow something like:

InitializeLazyDataprovider [a=er, b=12, c=hwhr]
BeforeClassLazyDataprovider [a=er, b=12, c=hwhr]
TestLazyDataprovider [a=er, b=12, c=hwhr]
InitializeLazyDataprovider [a=asd, b=234, c=wrhera]
BeforeClassLazyDataprovider [a=asd, b=234, c=wrhera]
TestLazyDataprovider [a=asd, b=234, c=wrhera]
...

Regards,

Tamas


So every test is executed

Tamás Kende

unread,
Oct 18, 2011, 5:44:42 AM10/18/11
to testng-users
Sorry for giving unclear inputs :), I tried to make the message
short...

I tried the suggested parameter, but it does not have any effect if I
set up parallel running (parallel=classes, thread-count=2). Without
this it works as I want.

Is it possible to achieve the same order with parallel test run?

And is it possible to make the test class initialization lazy
(referring to my message sent at 18 Oct 2011 00:15:29 in the same
thread)? Currently before running any tests (or before class methods)
TestNG creates more than 2 million test class (the dataprovider
returns with 81 records), I don't know why, they are all in in an
ArrayList of Object[]. I can't really see where all these instances
come from, I use Yourkit profiler, but I'm a noobie. It allocates more
than 1 GB. Probably it happens in the FactoryMethod class called by
TestNGClassFinder..

Thank you,
Tamas

Cédric Beust ♔

unread,
Oct 18, 2011, 12:28:30 PM10/18/11
to testng...@googlegroups.com
On Tue, Oct 18, 2011 at 2:44 AM, Tamás Kende <kende...@gmail.com> wrote:
Sorry for giving unclear inputs :), I tried to make the message
short...

Understood. It's just that when you say "the output is this when it should be that", it's not clear to me what code is running it and I have to go back a few messages and try to guess. The best way is to include a small and very specific piece of code along with the outputs.
 

I tried the suggested parameter, but it does not have any effect if I
set up parallel running (parallel=classes, thread-count=2). Without
this it works as I want.

Is it possible to achieve the same order with parallel test run?

And is it possible to make the test class initialization lazy
(referring to my message sent at 18 Oct 2011 00:15:29 in the same
thread)? Currently before running any tests (or before class methods)
TestNG creates more than 2 million test class (the dataprovider
returns with 81 records), I don't know why, they are all in in an
ArrayList of Object[]. I can't really see where all these instances
come from, I use Yourkit profiler, but I'm a noobie. It allocates more
than 1 GB. Probably it happens in the FactoryMethod class called by
TestNGClassFinder..

Are these coming from a data provider? If yes, is that data provider lazy? Based on your numbers, it absolutely should, and you should make sure that the iterator you return only creates a new test class in next() and not upfront.

-- 
Cédric


Tamás Kende

unread,
Oct 19, 2011, 7:59:07 AM10/19/11
to testng-users
Hi Cedric,

I attach a small project to reproduce my problems.The first problem
is, which more likely a bug, is when you run your tests in parallel,
the group-by-instances parameter has no effect, or it is not clear
which test runs on which thread and why. Instead of BeforeClass+Test
+Test2+Test3 runs on the same thread, you will find in some cases the
thread IDs are mixed:

The 2nd word is the thread id.

BeforeClass 11 LazyDataprovider [a=ewerr, b=6512, c=w56ua]
Test 11 LazyDataprovider [a=ewerr, b=6512, c=w56ua]
Test2 12 LazyDataprovider [a=ewerr, b=6512, c=w56ua]
Test3 13 LazyDataprovider [a=ewerr, b=6512, c=w56ua]

My other problem is with the test initialization (creating test
objects), if I use lazy iterators (implemented myself, truly works.
Why is not the testng creates test instances shortly before the test
runs (invoking test methods) instead of creating all instances at the
beginning? I could not reproduce the huge number of created test
instances with a new project. I will try it again later, but at the
moment I don't have time to work on it.

So in nutshell: can we do the test class initialization lazy? and can
we use group-by-instances with parallel tests?

Thanks

Tamas

Tamás Kende

unread,
Oct 19, 2011, 8:01:18 AM10/19/11
to testng...@googlegroups.com
Sorry, I forget the attachment...

TestngDebug.zip

Tamás Kende

unread,
Oct 19, 2011, 8:49:12 AM10/19/11
to testng-users
I'm so sorry, pls use the package testngdebug4... I forget to remove
the others....

On Oct 19, 2:01 pm, Tamás Kende <kende.ta...@gmail.com> wrote:
> Sorry, I forget the attachment...
>
>  TestngDebug.zip
> 16KViewDownload

queenstownswords

unread,
Mar 13, 2014, 5:44:47 PM3/13/14
to testng...@googlegroups.com
Hello,

Was there a resolution to this one?  I have hit the same type of issue with 2.40.0 and testng 6.8.8.

Thanks

Mrunal Gosar

unread,
Oct 15, 2014, 11:08:57 AM10/15/14
to testng...@googlegroups.com
Hey you created all the object upfront. You should be creating your new Object[]{....} in the next method. and control creation and movement of Objects using some kind of index or counters. See if below helps you in understanding the lazy data provider concept:

import java.util.Iterator;


@SuppressWarnings("rawtypes")
public class AccountIterator implements Iterator
{
 
static final int MAX=4;
 
int counter=0;
 
@Override
 
public boolean hasNext()
 
{
 
return counter<MAX;
 
}


 
@Override
 
public Object next()
 
{
 counter
++;
 
System.out.println("Created.");
 
return new Object[]{"stringA"+counter,"stringB"+counter};
 
}


 
@Override
 
public void remove()
 
{
 
throw new UnsupportedOperationException();
 
}
}


import java.util.Iterator;


import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;


public class TestNGLazyDPTest
{
 
@SuppressWarnings("rawtypes")
 
@DataProvider
 
public Iterator lazyDP()
 
{
 
return new AccountIterator();
 
}
 
 
@Test(dataProvider="lazyDP")
 
public void test(String stringA,String stringB)
 
{
 
System.out.println(stringA+" "+stringB);
 
}
}


Let me know if you need further help.
Reply all
Reply to author
Forward
0 new messages