Mock/Stub super constructor invocation in Java for unit testing - java

I want to unit-test my class with JUnit and EasyMock. It extends android.location.Location. But I am always getting Stub! exception because most of Android methods are not available in JVM runtime.
public class MyLocation extends Location {
public MyLocation(Location l) {
super(l);
}
public boolean methodUnderTest() {
return true;
}
}
I've tried to mock constructor invocation using Powermock, but it looks like it does not work for super calls. My test:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Location.class)
public class MyLocationTest {
#Test
public void methodUnderTestReturnsTrue() throws Exception {
Location locationMock = EasyMock.createMock(Location.class);
expectNew(Location.class, Location.class).andReturn(locationMock);
MyLocation myLocation = new MyLocation(locationMock);
assertTrue(myLocation.methodUnderTest());
}
}
An exception I am getting:
java.lang.RuntimeException: Stub!
at android.location.Location.<init>(Location.java:6)
Obviously the solution is to execute this test in Android runtime (i.e. start Android Simulator). But I don't like this approach because it takes quite a few time to start such test suite. Is there a way to stub super invocation or probably there's better approach in testing such implementations?

Taken straight from the Powermocks documentation.
Testing can then be done without invoking the EvilParent constructor.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ExampleWithEvilParent.class)
public class ExampleWithEvilParentTest {
#Test
public void testSuppressConstructorOfEvilParent() throws Exception {
suppress(constructor(EvilParent.class));
final String message = "myMessage";
ExampleWithEvilParent tested = new ExampleWithEvilParent(message);
assertEquals(message, tested.getMessage());
}
}

Related

Unable to mock System class static method using PowerMockito

Even though I have read the manual and gone through multiple answers for Powermock, could not mock a static method for my use case.
Class:
#Component
public class SCUtil{
public void createSC(){
try {
String host = InetAddress.getLocalHost().getHostAddress();
// ...
// ...
// ...
} catch (UnknownHostException e) {
log.error("Exception in creasting SC");
throw new ServiceException(e);
}
}
}
Test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest( InetAddress.class )
public class SCUtilTest {
#InjectMocks
private SCUtil scUtil;
private Event event;
#Before
public void beforeEveryTest () {
event = new InterventionEvent();
}
#Test(expected = ServiceException.class)
public void testCreateSC_Exception () {
PowerMockito.mockStatic(InetAddress.class);
PowerMockito.when(InetAddress.getLocalHost()).thenThrow(new UnknownHostException("test"));
scUtil.createSC(event);
}
}
Here, the test is failing as no exception is being thrown:
java.lang.AssertionError: Expected exception:
com.example.v1.test.selftest.errorhandling.ServiceException
I have wrecked more than a couple of hours in this and still have not gotten it to work. What am I doing wrong?
Thank you for all the help in advance :)
java.net.InetAddress is a system class. The caller of the system class should be defined in #PrepareForTest({ClassThatCallsTheSystemClass.class}).
See documentation.
The way to go about mocking system classes are a bit different than
usual though. Normally you would prepare the class that contains the
static methods (let's call it X) you like to mock but because it's
impossible for PowerMock to prepare a system class for testing so
another approach has to be taken. So instead of preparing X you
prepare the class that calls the static methods in X!
Please note #InjectMocks annotation does not inject static mocks, it can be removed.
Example of working test:
#RunWith(PowerMockRunner.class)
#PrepareForTest(SCUtil.class)
public class SCUtilTest {
private SCUtil scUtil = new SCUtil();
#Test(expected = ServiceException.class)
public void testCreateSC_Exception () throws UnknownHostException {
PowerMockito.mockStatic(InetAddress.class);
PowerMockito.when(InetAddress.getLocalHost()).thenThrow(new UnknownHostException("test"));
scUtil.createSC();
}
}

Starting a test with ActivityScenarioRule - AndroidStudio

I'm trying to set up some basic instrumented tests which I am running with AndroidStudio on my physical device.
The test is fairly straightforward:
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
private ActivityScenarioRule<MyActivity> scenarioRule = new ActivityScenarioRule<>(MyActivity.class);
#Test
public void onFoo() {
ActivityScenario scenario = scenarioRule.getScenario();
}
}
When it runs, this happens:
java.lang.NullPointerException: throw with null exception
at androidx.test.internal.util.Checks.checkNotNull(Checks.java:34)
at androidx.test.ext.junit.rules.ActivityScenarioRule.getScenario(ActivityScenarioRule.java:118)
at com.me.sample.activities.MyActivityTest.onFoo(MyActivityTest.java:...)
According to the documents, NullPointerException is thrown if you use getScenario() when no test is running. I am running these tests with the AndroidStudio UI and the tests are appearing in the test running window, so it doesn't make sense to me that it's throwing that exception.
Do I need to add a before() method to set up the scenarioRule? Is there some setting I'm missing? Or could this be caused by a dependency or something in MyActivity?
I was missing annotations. This is the version which works:
#RunWith(AndroidJUnit4.class)
#SmallTest
public class MyActivityTest {
#Rule
public ActivityScenarioRule<MyActivity> activityRule = new ActivityScenarioRule<>(MyActivity.class);
#Test
public void foo() {
ActivityScenarioRule<MyActivity> scenario = activityRule.getScenario()
}
}

Powermock can't mock static class

I have a class with code similar to :
public class class1{
private static final ConfigurationService config = Util.getInstance(ConfigurationService.class);
private SendQueueMessages sender;
public void start() throws LifecycleException{
LOGGER.info("Starting");
final ActiveMq activemq = config.getConfiguration().getActiveMq();
sender = new SendQueueMessages(activemq.getQueueName());
}
}
Elsewhere in the program Guice is being used to bind the configuration service and Util like so:
Util.register(new ThingICantChange(){
#Override
protected void configure (){
super.configure();
bind(ConfigurationService.class).to(ConfigurationServiceImpl.class).asEagerSingleton();
}
});
Is this possible to unit test? I was initially trying to use JUnit 5 and mockito, but it became apparent that I needed to mock static classes/methods (IoCUtils) and switched to JUnit4 for PowerMock.
I have tried:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Util.class)
public class Class1Test{
#Test
public void canStart(){
mockStatic(Util.class);
when(Util.getInstance(ConfigurationService.class)).thenReturn(new ConfigurationService);
Class1 class = new Class1();
class.start();
//etc.
}
}
However this just gives me an error about Util not prepared for test. Changing mockStatic() to PowerMockito.spy() did get me to the when, but then throws a null pointer error.
I found a solution, though I have mixed feelings about it.
Using the Util.register (the 2nd code block) I registered a configuration service implementation that created the mock objects. This worked and let me test the start() method, but feels kind of against the idea of a unit test.
public class ConfigServiceTest implements ConfigurationService{
#Override
public Configuration getConfiguration() {
Configuration conf = mock(Configuration.class);
ActiveMq amq = mock(ActiveMq.class);
when(amq.getQueueName()).thenReturn("test");
when(amq.getBrokerUrl()).thenReturn("http://127.0.0.1:61616?soTimeout=1000");
when(conf.getActiveMq()).thenReturn(amq);
return conf;
}
//other methods just allowed to return null
}
Then in the test:
Util.register(new thingICantChange(){
#Override
protected void configure (){
super.configure();
bind(ConfigurationService.class).to(ConfigServiceTest.class).asEagerSingleton();
}
});
class1 service = new class1();
service.start();
Assert.assertEquals(true, true);
start is void and not int a new thread so Assert.assertEquals(true,true) is the best anyone around me knew to check that start ran. Mockito/PowerMock times(1) would require a mock of class1 which seems rather counter to a unit test to see IF it can run.

Singleton returning new instance when accessed from test method

I am using Junit 4.12 with PowerMock 1.6 with Mockito. I have also used PowerMockRule library as described here. I am trying to execute initialization code for all of my test cases exactly once as described in this SO Thread. Its executing the initialization code exactly one time however, if I do ServiceInitializer.INSTANCE inside test method it returns me new object. I am not able to understand this behavior. Does anyone have any idea why this is happening? If I execute my code without PowerMockRule Library and run my test with PowerMockRunner then it works fine but in that case my ClassRule is not getting executed.
public class ServiceInitializer extends ExternalResource {
public static final TestRule INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
#Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this); //Print Address #3702c2f1
}
#Override protected void after() {
}
}
class BaseTest{
#Rule
public PowerMockRule powerMockRule = new PowerMockRule();
#ClassRule
public static final TestRule serviceInitializer = ServiceInitializer.INSTANCE;
#Before
public final void preTest() {
// some code
}
#After
public final void postTest() {
//some code
}
}
#PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
#Test
public void testMethodA_1(){
System.out.println(ServiceInitializer.INSTANCE);//Print Address #54d41c2b
}
}
Update
I printed the classloader for the classes and it turns out for first print statement the classloder was sun.misc.Launcher$AppClassLoader and for the second print statement the classloder was org.powermock.core.classloader.MockClassLoader. How can I solve this?
You don't have a singleton. You have a static INSTANCE variable. Keep in mind that one of those can exist for every classloader you have.
Instead make an enum of ServiceInitializer, like so
public enum ServiceInitializer {
INSTANCE;
// rest of class goes here
}
And rely on the JVM's language contracts to ensure the singleton.
Or, better yet, write your code to handle situations where more than one ServiceInitializer can exist, but it just happens that your program only uses one instance. This is the ideal choice, allowing you to alternate between the real ServiceInitializer and a mock if desired.
Edwin is correct; this is an issue with PowerMock creating a new ClassLoader for every test. I strongly recommend refactoring your code so it can be tested without PoeerMock and switch to Mockito.
These books may be helpful
Working Effectively With Legacy Code
Refactoring to Patterns
In the mean time, you can reference ServiceInitializer from your base class:
public class ServiceInitializer extends ExternalResource {
public static final ServiceInitializer INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
#Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this);
}
#Override protected void after() {
}
}
class BaseTest{
#Rule
public PowerMockRule powerMockRule = new PowerMockRule();
#ClassRule
public static final ServiceInitializer serviceInitializer = ServiceInitializer.INSTANCE;
#Before
public final void preTest() {
// some code
}
#After
public final void postTest() {
//some code
}
}
#PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
#Test
public void testMethodA_1(){
System.out.println(serviceInitializer);
}
}
Well I finally found the work around for this problem. As explained in my question my class was getting loaded by two different class loaders and thus causing problems for me. In order to resolve my issue I used #PowerMockIgnore annotation in order to defer its loading as follows:
#PowerMockIgnore({"com.mypackage.*"})
class BaseTest{
// Stuff goes here
}
This annotation tells PowerMock to defer the loading of classes with the names supplied to value() to the system classloader. You can read about this annotation from here.

Mocking a class object using Mockito and PowerMockito

Is it possible to mock a class object using Mockito and/or PowerMockito?
Something like:
Class<Runnable> mockRunnableClass = mock(Class<Runnable>.class);
An alternative to mocking Class might be to use a Factory instead. I know you are concerned about refactoring, but this could be done without changing the public API of the class. You haven't provided much code to understand the class you are trying to test, but here's an example of refactoring without changing the API. It's a trivial class, but it might give you an idea.
public class Instantiator {
public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
return runnableClass.newInstance();
}
}
Of course, the easiest thing to do to test this trivial class would be to use a genuine Runnable class, but if you tried to mock the Class, you would run into the problems you're having. So, you could refactor it thus:
public class PassThruFactory {
public Object newInstance(Class<?> clazz) throws Exception {
return clazz.newInstance();
}
}
public class Instantiator {
private PassThruFactory factory = new PassThruFactory();
public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
return (Runnable)factory.newInstance(runnableClass);
}
}
Now Instantiator does exactly the (trivially simple) thing it was doing before with the same public API and no need for any client of the class to do any special injecting of their own. However, if you wanted to mock the factory class and inject it, that's very easy to do.
why not using an agent if you can't refactor the code there isn't many options, as #jherics mentionned, java system classes are loaded by the bootstrap classloader and powermock can't redefine their bytecode.
However Powermock now coms with an agent, that will allow system classes mock. Check here for complete explanation.
The main idea is to modify your java command and add :
-javaagent: path/to/powermock-module-javaagent-1.4.12.jar
The basic thing this agent is doing is to definalize classes, to allow future mocking in a specific test, that's why you'll need to use specific types to communicate with the agent, for example with JUnit :
#Rule PowerMockRule rule = new PowerMockRule(); // found in the junit4 rule agent jar
TestNG is also supported. Just check the wiki page for more information.
Hope that helps.
First, as stated in the comments, you would need to do:
Class<Runnable> mockRunnableaClass = (Class<Runnable>)mock(Class.class);
But that won't work in the usual way because of a limitation with PowerMock. You cannot simply mock classes in from java.lang, java.net, java.io or other system classes because they're loaded by Java's bootstrap classloader and cannot be byte-code manipulated by PowerMock's classloader. (See PowerMock FAQ #4.) As of PowerMock 1.2.5, you can work around this. If the class you wanted to test was this:
public class ClassToTest {
private Class<Runnable> runnableClass;
public void setRunnableClass(Class<Runnable> runnableClass) {
this.runnableClass = runnableClass;
}
public Runnable foo() {
return runnableClass.newInstance();
}
}
Then you would do this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ClassToTest.class }) // Prepare the calling class for test
public class SystemClassUserTest {
#Test
public void testFoo() throws Exception {
Class<Runnable> mockClass = (Class<Runnable>) mock(Class.class);
Runnable mockRunnable = mock(Runnable.class);
ClassToTest objectUT = new ClassToTest();
objectUT.setRunnableClass(mockClass);
when(mockClass.newInstance()).thenReturn(mockRunnable);
assertThat(objectUT.foo(), is(sameInstance(mockRunnable);
}
}
How about this. creating a get method of the has a Object (MS) in class PCService and then mock it.
public class PCService implements PCServiceIf {
public MSIf getMS() {
return ms;
}
private MSIf ms = new MS();
public boolean isMovieAccessibleToMyLevel(String myLevel, String movieId) {
return getMS().getPCL(movieId);
}
}
#Test
public void testIsMovieAccessibleToMyLevelMock() {
msMock = mock(MS.class);
spy = spy(new PCService());
doReturn(msMock).when(spy).getMS();
when(msMock.getPCL(movieId)).thenReturn(value);
when(spy.getMS().getPCL(movieId)).thenReturn(value);
assertTrue(spy.isMovieAccessibleToMyLevel("PG", movieId) == true);
}

Categories

Resources