Java: How to Mock a protected method inside a static child class - java

I am having a protected method inside a static child class. I am running a testcase , its getting successful but code coverage is not increasing.
public class A{
private static final String var1 = "key1";
protected static class B extends OsCmd{
private String abc1;
private String abc2;
protected B(String xyz, String xyz2) {
this.abc1 = xyz;
this.abc2 = xyz2;
}
#Override
protected void updateEnv(Map<String, String> env) {
env.put(VAR1, "FALSE");
env.put(VAR2, "TRUE");
env.put("key3", abc1);
env.put("key4", abc2);
}
}
}
Below is my test case
#ExtendWith(MockitoExtension.class)
public class ATest {
private A mockA;
#BeforeEach
public void setup() {
mockA = Mockito.spy(A.class);
}
#Test
public void test2() {
try (MockedConstruction mockedConstruction =
mockConstruction(A.B.class)) {
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
A.B mockB =
new A.B("a", "b");
//doNothing().when(mockB).updateEnv(map);
mockB.updateEnv(map);
}
}
}
Can someone please help here, what mistake i am doing?

When you mock the constructor, then all internal method calls are also mocked and do not go through the actual code.
If you remove the following try-with-resources:
try (MockedConstruction mockedConstruction =
mockConstruction(A.B.class))
The real code will be executed and the coverage will increase.

Related

How to fully test coverage a constructor that has a System.getenv("name") operation inside

I am using JUNIT5, have been trying to fully coverage a piece of code that involves System.getenv(""); I writed a couple classes to replicate what I am experiencing right now and so you can use them to understand me also (minimal reproducible example):
First we have the service I need to get with full coverage (ServiceToTest.class) (it has a CustomContainer object which contains methods that it needs):
#Service
public class ServiceToTest {
private final CustomContainer customContainer;
public ServiceToTest() {
Object configuration = new Object();
String envWord = System.getenv("envword");
this.customContainer = new CustomContainer(configuration, envWord == null ? "default" : envWord);
}
public String getContainerName() {
return customContainer.getContainerName();
}
}
CustomContainer.class:
public class CustomContainer {
#Getter
String containerName;
Object configuration;
public CustomContainer(Object configuration, String containerName) {
this.configuration = configuration;
this.containerName = containerName;
}
}
I have tried using ReflectionTestUtils to set the envWord variable without success... I tried this https://stackoverflow.com/a/496849/12085680, also tried using #SystemStubsExtension https://stackoverflow.com/a/64892484/12085680, and finally I also tried using Spy like in this answer https://stackoverflow.com/a/31029944/12085680
But the problem is that this variable is inside the constructor so this only executes once and I think that it happens before any of this configs I tried before can apply, here is my test class:
#ExtendWith(MockitoExtension.class)
class TestService {
// I have to mock this becase in real project it has methods which I need mocked behavour
private static CustomContainer mockCustomContainer = mock(CustomContainer.class);
// The serviceToTest class in which I use ReflectionTestUtils to use the mock above
// Here is where the constructor gets called and it happens BEFORE (debuged) the setup method
// which is anotated with #BeforeAll
private static ServiceToTest serviceToTest = new ServiceToTest();
#BeforeAll
static void setup() {
// set the field customContainer at serviceToTest class to mockCustomContainer
ReflectionTestUtils.setField(serviceToTest, "customContainer", mockCustomContainer);
}
#Test
void testGetContainerNameNotNull() {
assertNull(serviceToTest.getContainerName());
}
}
I need to write a test in which serviceToTest.getContainerName is not null but the real purpose of this is to have coverage of this sentence envWord == null ? "default" : envWord so it would be a test that is capable of executing the constructor and mocking System.getenv() so that it returns not null...
Right now the coverage looks like this and I can not find a way to make it 100% Any ideas??
EDIT:
So after following tgdavies suggestion, the code can be 100% covered, so this is the way:
Interface CustomContainerFactory:
public interface CustomContainerFactory {
CustomContainer create(Object configuration, String name);
}
CustomContainerFactoryImpl:
#Service
public class CustomContainerFactoryImpl implements CustomContainerFactory {
#Override
public CustomContainer create(Object configuration, String name) {
return new CustomContainer(configuration, name);
}
}
EnvironmentAccessor Interface:
public interface EnvironmentAccessor {
String getEnv(String name);
}
EnvironmentAccessorImpl:
#Service
public class EnvironmentAccessorImpl implements EnvironmentAccessor {
#Override
public String getEnv(String name) {
return System.getenv(name);
}
}
Class ServiceToTest after refactoring:
#Service
public class ServiceToTest {
private final CustomContainer customContainer;
public ServiceToTest(EnvironmentAccessor environmentAccessor, CustomContainerFactory customContainerFactory) {
Object configuration = new Object();
String envWord = environmentAccessor.getEnv("anything");
this.customContainer = customContainerFactory.create(configuration, envWord == null ? "default" : envWord);
}
public String getContainerName() {
return customContainer.getContainerName();
}
}
Finally the test case after refactoring (here is were I think it can be improved maybe?):
#ExtendWith(MockitoExtension.class)
class TestService {
private static CustomContainer mockCustomContainer = mock(CustomContainer.class);
private static CustomContainerFactory customContainerFactoryMock = mock(CustomContainerFactoryImpl.class);
private static EnvironmentAccessor environmentAccessorMock = mock(EnvironmentAccessorImpl.class);
private static ServiceToTest serviceToTest;
#BeforeAll
static void setup() {
when(environmentAccessorMock.getEnv(anyString())).thenReturn("hi");
serviceToTest = new ServiceToTest(environmentAccessorMock, customContainerFactoryMock);
ReflectionTestUtils.setField(serviceToTest, "customContainer", mockCustomContainer);
when(serviceToTest.getContainerName()).thenReturn("hi");
}
#Test
void testGetContainerNameNotNull() {
assertNotNull(serviceToTest.getContainerName());
}
#Test
void coverNullReturnFromGetEnv() {
when(environmentAccessorMock.getEnv(anyString())).thenReturn(null);
assertAll(() -> new ServiceToTest(environmentAccessorMock, customContainerFactoryMock));
}
}
Now the coverage is 100%:
EDIT 2:
We can improve the test class and get the same 100% coverage like so:
#ExtendWith(MockitoExtension.class)
class TestService {
private static CustomContainer mockCustomContainer = mock(CustomContainer.class);
private static IContainerFactory customContainerFactoryMock = mock(ContainerFactoryImpl.class);
private static IEnvironmentAccessor environmentAccessorMock = mock(EnvironmentAccessorImpl.class);
private static ServiceToTest serviceToTest;
#BeforeAll
static void setup() {
when(environmentAccessorMock.getEnv(anyString())).thenReturn("hi");
when(customContainerFactoryMock.create(any(), anyString())).thenReturn(mockCustomContainer);
serviceToTest = new ServiceToTest(environmentAccessorMock, customContainerFactoryMock);
}
#Test
void testGetContainerNameNotNull() {
assertNotNull(serviceToTest.getContainerName());
}
#Test
void coverNullReturnFromGetEnv() {
when(environmentAccessorMock.getEnv(anyString())).thenReturn(null);
assertAll(() -> new ServiceToTest(environmentAccessorMock, customContainerFactoryMock));
}
}
Refactor your code to make it testable, by moving object creation and static method calls to components, which you can mock in your tests:
interface ContainerFactory {
CustomContainer create(Object configuration, String name);
}
interface EnvironmentAccessor {
String getEnv(String name);
}
#Service
public class ServiceToTest {
private final CustomContainer customContainer;
public ServiceToTest(ContainerFactory containerFactory, EnvironmentAccessor environmentAccessor) {
Object configuration = new Object();
String envWord = environmentAccessor.getEnv("envword");
this.customContainer = containerFactory.create(configuration, envWord == null ? "default" : envWord);
}
public String getContainerName() {
return customContainer.getContainerName();
}
}

Unit test with JUnit 5

Hi all i receive Nullpointer when trying to execute this unit test.I want to test e class which receive 3 parameters and returns a string. I think i need to make #Before or something else but it didn't works. Do you have suggestions...Thanks !
public class UrlConstructorTest {
private UrlConstructor urlConstructor;
#Before
public void setUp() {
urlConstructor = new UrlConstructor();
}
public static final String TEST_UPDATE_MANIFEST_SR = "/packages/proxyId/test/test1/123/test3/test_test";
#Test
public void constructUpdateManifestSrInSasTokenTest() {
String result = urlConstructor.composeDeviceRegistrationUrl("test","test123","test");
System.out.println(result);
assertNotNull(result);
assertEquals(TEST, result);
}
}
UrlConstructor is define like this:
#Component
public class UrlConstructor {
And this is the method in this class:
public String composeDUrl(String deviceId, String scopeId) {
return String.format(Constants.socpe, tes, test);
}
In Junit5, you should be using #BeforeEach. Or you can get rid of that setUp method completely.
public class UrlConstructorTest {
private final UrlConstructor urlConstructor = new UrlConstructor();
public static final String TEST_SR = "/packages/proxyId/testID/product/testscope/testcomponent/coomponent_up";
#Test
public void constructTest() {
String result = urlConstructor.composeDeviceRegistrationUrl("testID","coomponent_up","testscope");
System.out.println(result);
assertNotNull(result);
assertEquals(TEST_SR, result);
}
}

Mock returns Wrong Collection

I want to return a filled Map with my mocked Object, but the size of the Map is always Null. The mocked Object "CommandLineValues options" is not Null and also the Boolean variable "doCleanFirst" I can mock successfully.
Here is my Testclass:
#RunWith(MockitoJUnitRunner.class)
public class IndexBMECatTest {
#InjectMocks
private IndexBMECat classUnderTest;
#Mock
private CommandLineValues options;
#Test
public void testAccessoryItemHasNoDublicates() {
Map<String, String> testMap = new HashMap<>();
testMap.put("key", "value");
when(options.getCleanFirst()).thenReturn(false);
when(options.readWhitlist()).thenReturn(testMap);
classUnderTest.run();
}
}
Here is the constructor of my class where the code start, the tested Method is not relevant:
private boolean doCleanFirst;
private Map<String, String> whiteList;
public IndexBMECat(TransportClient client, CommandLineValues options, BMECatReader reader) throws Exception {
this.doCleanFirst = options.getCleanFirst();
this.whiteList = options.readWhitlist();
if (whiteList.isEmpty()) {
throw new Exception("Missing whiteList");
}
}
I also tried other variants:
Mock the Map and the return value of the method "isEmpty"
Initialize the Testclass and give the mocked Object to the constructor
But the whiteList has always the size = 0
Thx, this works now:
private IndexBMECat classUnderTest;
#Mock
private CommandLineValues options;
#Mock
private BMECatReader reader;
#Mock
TransportClient client;
#Before
public void setUp() throws Exception {
Map<String, String> testMap = new HashMap<>();
testMap.put("key", "value");
when(options.getCleanFirst()).thenReturn(false);
when(options.readWhitlist()).thenReturn(testMap);
classUnderTest = new IndexBMECat(client, options, reader);
}
#Test
public void testAccessoryItemHasNoDublicates() {
classUnderTest.run();
}
First I mock the methods which will be executed in the contructor and then I create the instance of my testclass.

Mock private static method in final class using PowerMockito

I have a final class with private static method which is invoked inside another static method
public final class GenerateResponse{
private static Map<String, String> getErrorDetails(JSONObject jsonObject) {
// implementation
}
public static String method1(params...){
Map<String, String> map = getErrorDetails(new JsonObject());
// implementation
}
}
I need to mock the private static method call getErrorDetails(), but my test is calling the actual method. Here is my code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(GenerateResponse.class)
public class GenerateResponseTest{
#Test
public void testFrameQtcErrorResponse() throws Exception {
Map<String, String> errorDtls = new HashMap<String, String>();
PowerMockito.spy(GenerateResponse.class);
PowerMockito.doReturn(errorDtls).when(GenerateResponse.class, "getErrorDetails", JSONObject.class);
String response = GenerateResponse.method1(params...);
}
You should use an argument matcher in the whenmethod. I've modified your code a little bit to run the test case.
Actual method
public final class GenerateResponse{
private static Map<String, String> getErrorDetails(JSONObject jsonObject) {
return null;
}
public static String method1() {
Map<String, String> map = getErrorDetails(new JSONObject());
return map.get("abc");
}
}
Test method
#RunWith(PowerMockRunner.class)
#PrepareForTest(GenerateResponse.class)
public class GenerateResponseTest {
#Test
public void testFrameQtcErrorResponse() throws Exception {
Map<String, String> errorDtls = new HashMap<String, String>();
errorDtls.put("abc", "alphabets");
PowerMockito.mockStatic(GenerateResponse.class, Mockito.CALLS_REAL_METHODS);
PowerMockito.doReturn(errorDtls).when(GenerateResponse.class,
"getErrorDetails", Matchers.any(JSONObject.class));
String response = GenerateResponse.method1();
System.out.println("response =" + response);
}
}
Output
response =alphabets

Define expectations for public final instance variable

I'm new at unit testing and am running into an issue with jMock that I can't seem to figure out. I have a public final instance variable which I need to define an expectation for, but I can't get it to work. If I make a getter for the variable, it works, but I'd rather not have to create a bunch of getters just to make unit testing work. Any help on how to do this would be much appreciated. Here's some code illustraiting what I'm trying to do:
public class Main {
private SimpleObject simpleObject;
public Main(SimpleObject o){
this.simpleObject = o;
}
public int iDontWork(){
return simpleObject.myList.size();
}
public int iWork(){
return simpleObject.getMyList().size();
}
}
My test:
#RunWith(JMock.class)
public class MainTest {
Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
#Mock
SimpleObject simpleObject;
private Main main;
#Before
public void setup(){
main = new Main(simpleObject);
}
#Test
public void itWorks() {
context.checking(new Expectations() {{
oneOf(simpleObject).getMyList();
will(returnValue(new ArrayList<String>(Arrays.asList("Hey"))));
}});
int i = main.iWork();
assertEquals(1, i);
}
#Test
public void itDoesntWork() {
context.checking(new Expectations() {{
oneOf(simpleObject).myList.size(); will(returnValue(1));
}});
int i = main.iDontWork();
assertEquals(1, i);
}
}
SimpleObject:
public class SimpleObject {
public final List<String> myList;
public SimpleObject(){
myList = Collections.unmodifiableList(Arrays.asList("Hey"));
}
public List<String> getMyList(){
return myList;
}
}
A mock object implements methods of the real object. It does not have fields of the real object (even if these fields are public).

Categories