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

Time:11-06

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 coverage

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%:

coverage100

CodePudding user response:

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();
    }
}
  • Related