How to mock getResourceAsStream in the static block?
I think it is untestable.
I reviewed SO and cannot find the answer. The closes-SO-post-here does not address the issue as the call to getResourceAsAStream in the post is not from a static block.
I tried PowerMock, and run into number of limitations. First if I want to mock SomeProperties.class.getResourceAsStream - the static block will execute, as I will need to refer to the class itself. I can suppress static block to prevent doing so, but this will prevent me from getting the static block to execute at all. The solution would be to postpone the execution of the static block until after someProperties.class.getResourceAsStream is mocked.
I do not think it is possible though.
It seems that this code is purely untestable;
Any other ideas?
Here is the code [and a link to GITHUB]:
package com.sopowermock1;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class SomeProperties {
private static Properties props = new Properties();
static {
InputStream is = SomeProperties.class.getResourceAsStream("/some.properties");
try {
props.load(is);
System.out.println("Properties.props.keySet() = " + props.keySet());
} catch (IOException e) {
// How test this branch???
System.out.println("Yes. We got here.");
throw new RuntimeException(e);
}
}
private SomeProperties() {}; // to makes life even harder...
public static String getVersion() {
return props.getProperty("version");
}
}
And here is the test GITHUB Link
package com.sopowermock1;
import java.io.InputStream;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.sopowermock1.SomeProperties;
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeProperties.class)
// This will prevent running static block completely:
// #SuppressStaticInitializationFor("com.sopowermock1.SomeProperties")
public class SomePropertiesTest {
#Mock
private static InputStream streamMock;
#Before
public void setUp() {
MockitoAnnotations.initMocks(SomeProperties.class);
System.out.println("test setUp");
}
#Test(expected = RuntimeException.class)
public void testStaticBlock() {
PowerMockito.mockStatic(SomeProperties.class); // this will mock all static methods (unwanted as we want to call getVersion)
// This will cause static block to be called.
PowerMockito.when(SomeProperties.class.getResourceAsStream("/some.properties")).thenReturn(streamMock);
SomeProperties.getVersion();
}
}
Any ideas?. Full GITHUB source is here.
as mention in How to mock getResourceAsStream method using PowerMockito and JUnit?, use Extract Delegate , then mock the delegate class such as XXStreamFetcher, and then you can test it.
Related
I need to test a method which calls another private method, the problem is not to try to mock the result that the private method returns, but to do so not to obtain the coverage of jacoco or sonar. I tested with Powermock, with EasyMock but when using a spy, when use an spy the test is not covered by sonar or jacoco. The private method and the public method that make the call are at the same class.
Situation to test:
public class ClassOne {
private Object methodOne () {
.....
return object;
}
private Object mehodTwo () {
return Object name = mehtodOne ();
}
}
Test example:
package foo.bar;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(VlcPlayerMinimal.class)
public class VlcPlayerMinimalTest {
#Test
public void getVlcRcStatusTest() {
VlcPlayerMinimal vlcPlayerSpy = PowerMockito.spy(new VlcPlayerMinimal());
try {
PowerMockito.doReturn("{status: stopped, id: 2}").when(vlcPlayerSpy, "executeGetRequest", Mockito.any(), Mockito.any());
String vlcRcStatus = vlcPlayerSpy.getVlcRcStatus();
System.out.println(vlcRcStatus);
} catch (Exception e) {
e.printStackTrace();
fail("Unexpected exception thrown.");
}
}
}
No, it's not possible. Pick one or the other.
What would such "coverage" even prove? You are not actually covering it. Code coverage is a metric to help you identify spots that are not well tested, not one that you should try to trick into appearing higher than it really is.
I am writing a unit test to mock a static method in the verticle but getting ClassNotPreparedException always. I think that its only possible to mock this way if only the class is static, but i have non static class. What am i missing?
I have tried various solutions like using #rule or #PowerMockIgnore
//myVerticleTest.java
package com.blabla.me.verticles;
import static com.google.common.truth.Truth.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import io.vertx.core.Vertx;
import io.vertx.junit5.VertxTestContext;
import io.vulpx.VulpxTestBase;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.runner.RunWith;
import com.blabla.me.verticles.AdditionalInformationCardVerticle;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.junit.Rule;
import com.blabla.me.verticles.st;
#RunWith(PowerMockRunner.class)
#PrepareForTest({ st.class })
#PowerMockIgnore({"org.mockito.*"})
public class myVerticleTest extends VulpxTestBase {
#Rule public PowerMockRule rule = new PowerMockRule();
private Vertx vertx;
private AdditionalInformationCardVerticle dummy;
#BeforeEach
#PrepareForTest({ st.class })
public void setUp(VertxTestContext testContext) throws Exception {
vertx = Vertx.vertx();
try {
PowerMockito.mockStatic(st.class);
PowerMockito.when(st.createClient()).thenReturn("kk");
//deploying verticle
dummy = new AdditionalInformationCardVerticle();
vertx.deployVerticle(dummy, testContext.completing());
} catch (Exception e) {
System.out.println("heyyy eroorrr : " + e);
}
}
#Test
#PrepareForTest({ st.class })
public void justnormaltest() {
cla ownclass = new cla();
String k = ownclass.createfromclass();
assertThat("kk").isEqualTo(k);
}
}
// st.java
public class st {
public static String createClient() {
return "kk";
}
}
// cla.java
public class cla {
public String createfromclass() {
return st.createClient();
}
}
I expect it to run the assertion but i always get below excpetion:
"org.powermock.api.mockito.ClassNotPreparedException:
The class com.sap.me.verticles.st not prepared for test.
To prepare this class, add class to the '#PrepareForTest' annotation.
In case if you don't use this annotation, add the annotation on class or method level. "
Here:
#PrepareForTest({ st.class })
That one goes to exactly one place: in front of your test class public class myVerticleTest.
And hint: instead of adding more and more "things" to not working code: pick any good documentation, and try to follow that to the last ; in the example code (instead of assuming that adding more and more things here or there would help).
One good starting point: the official documentation on static mocking.
And of course, the usual caveat: consider not learning about PowerMock in the first place. Instead focus on writing "easy to test" code. Far too often, people think PowerMock(ito) is the answer to their problem. When their problem in reality is their inability to write "easy to test" production code.
I'm new to all the mentioned technologies so it might be a stupid question.
We have a spring boot application where we need to write to a PostgreSQL-Database via JDBC.
Therefore we need the static DriverManager.getConnection() method to open the connection.
Now in my unit tests I don't want to call this class directly.
Instead I want to check, that the DriverManager.getConnection() is called with the correct String as that is my expected observable external behavior.
I encapsulated this behavior into a ConnectionFactory with the method newConnection(ConnectionType.POSTGRESQL) because we got more than one Database to use in this Application.
Now I can't find a way to verify via Mockito that this external dependency was called with the correct String like you could with an instance:
DriverManager dm = mock(DriverManager);
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verify(dm).getConnection("theConnectionStringToBeExpected");
So how to do this with the static dependency?
I tried the Captor-way but this seems to only work for direct usage like
mockStatic(DriverManager.class);
final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// What I have to do to verify
DriverManager.getConnection("theActualConnectionString");
// What I would like to do to verify
// connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verifyStatic();
StaticService.getConnection(captor.capture());
assertEquals("theExpectedConnectionString", captor.getValue());
Edit:
Here is the nasty little workaround which I currently use for another server...
public void driverManagerIsCorrectlyCalledForAds() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
final Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:extendedsystems:advantage://server:1337/database_name;user=user;password=password;chartype=ansi"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.ADS);
assertEquals(expectedConnection, actualConnection);
}
Edit 2:
TestClass:
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.test.context.junit4.SpringRunner;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.*;
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#PrepareForTest({ConnectionFactory.class, DriverManager.class, DatabaseDriverInformation.class})
public class ConnectionFactoryTest {
#InjectMocks
ConnectionFactory connectionFactory;
#Mock
DatabaseDriverInformation databaseDriverInformation;
#Mock
DatabaseProperties databaseProperties;
#Mock
DatabaseProperties.Pg pg;
#Mock
DatabaseDriverLoader databaseDriverLoader;
#Before
public void setUp() {
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(ads).getServer();
doReturn(1338).when(ads).getPort();
doReturn("database_name").when(ads).getDatabasename();
doReturn("user").when(ads).getUser();
doReturn("password").when(ads).getPassword();
}
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:postgresql://server:1338/database_name;user=user;password=password"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.POSTGRESQL);
assertEquals(expectedConnection, actualConnection);
}
}
Class under Test:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.*;
#Service()
public class ConnectionFactory {
#Autowired
private DatabaseDriverLoader databaseDriverLoader;
#Autowired
DatabaseProperties databaseProperties;
public Connection newConnection(ConnectionType connectionType) {
databaseDriverLoader.load();
final String connectionString = connectionStringFor(connectionType);
try {
return DriverManager.getConnection(connectionString);
} catch (SQLException sqlException) {
throw new RuntimeException("Couldn't connect to Server");
}
}
private String connectionStringFor(ConnectionType connectionType) {
switch (connectionType) {
case ADS:
return this.adsConnectionString();
case POSTGRESQL:
return this.pgConnectionString();
default:
throw new RuntimeException("Invalid connection Type requested!");
}
}
private String adsConnectionString() {
return new StringBuilder()
.append("jdbc:extendedsystems:advantage://")
.append(databaseProperties.getAds().getServer())
.append(":")
.append(databaseProperties.getAds().getPort())
.append("/")
.append(databaseProperties.getAds().getDatabasename())
.append(";user=")
.append(databaseProperties.getAds().getUser())
.append(";password=")
.append(databaseProperties.getAds().getPassword())
.append(";chartype=ansi")
.toString();
}
private String pgConnectionString() {
return new StringBuilder()
.append("jdbc:postgresql://")
.append(databaseProperties.getPg().getServer())
.append(":")
.append(databaseProperties.getPg().getPort())
.append("/")
.append(databaseProperties.getPg().getDatabasename())
.append("?user=")
.append(databaseProperties.getPg().getUser())
.append("&password=")
.append(databaseProperties.getPg().getPassword())
.toString();
}
}
I removed the package-names, some specific imports and some unnecessary tests which are working.
After some search I found this: How to verify static void method has been called with power mockito
In Essence:
first:
mockStatic(ClassWithStaticFunctionToVerify.class)
second:
Execute the Code that will call the function you want to verify later
third:
verifyStatic();
ClassWithStaticFunctionToVerify.functionYouWantToVerify("ParameterValueYouExpect");
With it's help I got the following Solution which works fine:
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
// Arrange
mockStatic(DatabaseProperties.Pg.class);
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(pg).getServer();
doReturn(1338).when(pg).getPort();
doReturn("database_name").when(pg).getDatabasename();
doReturn("user").when(pg).getUser();
doReturn("password").when(pg).getPassword();
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).loadAdsDriverClass();
doNothing().when(databaseDriverLoader).loadPgDriverClass();
when(DriverManager.getConnection(anyString())).thenReturn(expectedConnection);
// Act
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
// Assert
verifyStatic();
DriverManager.getConnection("jdbc:postgresql://server:1338/database_name?user=user&password=password");
}
I'm reading this documentation on PowerMockito and it has two main examples:
Mocking static methods
Partially mocking a class
but I want to know how to mock an entire class that's created with new. I am looking for the PowerMockito version of Mockito's mock method. This should be able to replace new Foo() in my production code with a Mockito mock(Foo.class), somehow. Here's what I've tried:
import com.PowerMockitoProduction;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Matchers.any;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(PowerMockRunner.class)
#PrepareForTest(PowerMockitoProduction.class)
public class PowerMockitoTest {
#Test(expected = UnsupportedOperationException.class)
public void test() throws Exception {
HttpClient mock = PowerMockito.mock(HttpClient.class);
when(mock.executeMethod(any(HttpMethod.class))).thenThrow(UnsupportedOperationException.class);
new PowerMockitoProduction().createClient();
}
}
This test fails with this:
java.lang.Exception: Unexpected exception, expected<java.lang.UnsupportedOperationException> but was<java.lang.IllegalArgumentException>
Here's my production code:
package com;
import org.apache.commons.httpclient.HttpClient;
import java.io.IOException;
public class PowerMockitoProduction {
public void createClient() throws IOException {
HttpClient client = new HttpClient();
client.executeMethod(null);
System.out.println(client);
}
}
With my debugger, I can see that the client is not a mock, like I expected.
I've also tried using:
Object mock = PowerMockito.whenNew(HttpClient.class).withNoArguments().getMock();
But for some reason, that returns a mock that is not completely constructed. I've also tried this:
HttpClient mock = PowerMockito.whenNew(HttpClient.class).withNoArguments().thenReturn(mock(HttpClient.class)).getMock();
But that gives me a ClassCastException on that line. So, what is the correct way to mock out a class completely with PowerMockito?
Unlike this example implies, the reason I'm trying to mock out HttpClient is so that I can call verify it later.
You don't need to call getMock() method to get back the mocked object. Basically, mock an instance of HttpClient, store it in local variable and use that:
#Test(expected=UnsupportedOperationException.class)
public void test() {
HttpClient httpClient = mock(HttpClient.class);
PowerMockito.whenNew(HttpClient.class).withNoArguments().thenReturn(httpClient);
when(httpClient.executeMethod(any(HttpMethod.class))).thenThrow(UnsupportedOperationException.class);
new PowerMockitoProduction().createClient();
verify(httpClient).executeMethod(null);
}
Given this target code:
...
sessionWrapper.execute(arenaCreateCql, arenaGuid, arenaName, displayName, authorName, createdOn);
...
And Mockito code to validate that line:
...
#Captor
private ArgumentCaptor<Date> createdOnCaptor;
...
#Test
public void testThat_Execute_CreatesNewArena() throws Exception {
...
inOrder.verify(mockSessionWrapper).execute(
eq(arenaCreateCql), eq(testArenaGuid), eq(testArenaName), eq(testArenaDisplayName), eq(testAuthorName), createdOnCaptor.capture());
...
assertNotNull(createdOnCaptor.getValue());
}
This works using Mockito 1.9.5. When upgrading 1.10.8, the verify passes, but the getValue() fails with this error:
org.mockito.exceptions.base.MockitoException:
No argument value was captured!
You might have forgotten to use argument.capture() in verify()...
...or you used capture() in stubbing but stubbed method was not called.
Be aware that it is recommended to use capture() only with verify()
Edit to add MCVE. The following code runs green with Mockito 1.9.5, red with Mockito 1.10.8.
MockitoExample.java:
package org.makeyourcase.example;
import java.util.Date;
public class MockitoExample {
private MockitoExampleExecutor executor;
public void execute(){
executor.execute("var1", new Date());
}
}
MockitoExampleExecutor.java:
package org.makeyourcase.example;
public class MockitoExampleExecutor {
public void execute(Object... bindVariables){
}
}
MockitoExample_UT:
package org.makeyourcase.example;
import java.util.Date;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class MockitoExample_UT {
#Mock
private MockitoExampleExecutor mockitoExampleExecutor;
#Captor
private ArgumentCaptor<Date> dateCaptor;
#InjectMocks
private MockitoExample subject;
#Test
public void testThat_Execute_InvokesCalendar() throws Exception {
subject.execute();
verify(mockitoExampleExecutor).execute(eq("var1"), dateCaptor.capture());
assertNotNull(dateCaptor.getValue());
}
}
One other piece of info came to light as a result of creating the MCVE - the test works fine if the Date is the only element passed for bindVariables. That is, remove "var1" from target and test code, then the test runs fine under 1.9.5 and 1.10.8. Also, it doesn't matter that the captor is for a Date. The same issue occurs if the parameter is of another type, such as Integer.
Thanks, this is probably a bug, I've created the report on GH-188.
Not sure when it will be fixed though. Fixed in GH-211.