radar
radar

Reputation: 605

Powermockito expected object creation not working when expected in thread

I am using PowerMockito in some of my unit testing, and have run into a problem. I am attempting to test a method that creates a series of threads and runs them. Inside each thread, an object is created that I need to expect and instead return my own mock object (it makes http requests etc.). I've done this before using PowerMockito.whenNew(...), and it's worked fine, but this seems to not be working - does PowerMockito not work within threads? I haven't been able to find any other problems like this or documentation on the situation.

Some sample code for you to help visualize:

for(Object object : objectList) {
    Thread t = new Thread(new Runnable() {
        public void run() {
            SomeObject objectImTryingToStub = new SomeObject();
        }
    });
}

The code I'm using to try and catch that object creation:

SomeObject mockSomeObject = mock(SomeObject.class);
PowerMockito.whenNew(SomeObject.class).withNoArguments().thenReturn(mockSomeObject);

This same code has worked fine for other tests in the same class, which successfully caught the creation of the same type of object in the same manner.

Any help is appreciated! Thanks

Upvotes: 2

Views: 1070

Answers (5)

Charles Zhang
Charles Zhang

Reputation: 91

I've ran into the same problem for my project, and after doing some digging, it's actually not threading that is causing the problem per se, but that the object is created in an anonymous class derived from Runnable.

In PowerMock's doc, look at the second bullet point, you need to put the class that is doing the creation rather than the class that is being instantiated in PrepareForTest.

So you need to annotate your test using PrepareForTest to prepare whatever that annonymous class is, which is created at runtime. Generally, it would be Runnable$n where n is some number that is calculated for you at runtime. So how do you get the class which is not know statically? Luckily, PrepareForTest also accepts strings in its fullyQualifiedNames, so you would need to change your PrepareForTest to something like this:

@PrepareForTest( fullyQualifiedNames = {"com.whatever_package_that_SomeObject_is_in.*"}, value = {SomeStaticlyKnowClass.class} )

Upvotes: 5

goolie
goolie

Reputation: 165

Late to the party here but I think the proper solution is still missing. As you know, you have to PrepareForTest the class that does the object creation you are trying to mock. What is not so obvious is the class that is actually doing the creation here. That class is the inner anonymous Runnable class. The name of this class is generated at compile time and not easy to determine in the unit test. Assuming the class the snippet was pulled from is com.example.SomeClass the statement you would need could be something like:

@PrepareForTest(com.example.SomeClass$4.class)

But the easier way to do this is to use the other form of the annotation that allows for wildcards:

@PrepareForTest("com.example.*")

Just try to keep the wildcard matcher as tight as possible so PowerMock doesn't have to prepare every class under the sun to run the test.

Upvotes: 6

Andre Lehmann
Andre Lehmann

Reputation: 36

Recently I've faced the same problem and found a solution. What is really happens are that inner declaration of Runnable is kicking all your mocks out! 'Cause the Runnable class is loaded by bootstrap Classloader, PowerMock has no way to mock any action inside. The solution (to me) is declare the instantiation outside Runnable inner class.

Upvotes: 2

radar
radar

Reputation: 605

For anyone who has run into a similar problem, I've looked into it a little further and it seems that this feature doesn't fully work with threading. I tried to make it throw a FNF exception on creation - inside the thread it did nothing, while if i place the object creation before the threading segment, it works as expected. I'm not sure why this is the case, but at least I have a definitive answer now. If anyone knows a work-around for this, please let me know.

Upvotes: 1

David M. Karr
David M. Karr

Reputation: 15225

Since you're not showing very much of your code, I would have to guess that you're missing the "@PrepareForTest" entry for the class that is making the "new" call, likely your CUT (class under test).

Upvotes: 0

Related Questions