I am trying to write the test case for my application and I cannot get past a condition even after providing what is expected, from what I know.
Here is my test class.
#ExtendWith(MockitoExtension.class)
class AppConfigTest {
#Mock
#TempDir
File mockedFile;
#InjectMocks
private AppConfig appConfig;
#Test
void getData() throws Exception {
File f = new File("f");
File[] files = {f};
lenient().when(mockedFile.listFiles()).thenReturn(files);
lenient().when(mockedFile.isFile()).thenReturn(true);
assertNotNull(appConfig.getData());
}
}
My implementation. The test doesn't go past the if condition. The test does not cover the code after the condition as it turns true all the time. I need my test to cover keyMap() in the last line.
private Map<String, String> getData() {
File[] files = new File(APP_CONST.DIRECTORY).listFiles();
if (null == files) { // not turning FALSE even after providing mocked "files" array
return Collections.emptyMap();
}
List<String> keysList = getKeyList(files);
return keyMap(APP_CONST.DIRECTORY, keysList);
}
Can anyone please tell me how to correct this please? Using SpringBoot/JUnit 5
We discussed this in the comments, but in any case, I guess an example is better.
One way you could go about this is to make sure the same folder exists. In the test setup you could simply create it.
#Before
public void setUp() {
new File(APP_CONST.DIRECTORY).mkdirs();
}
Now when accessing it in the implementation there will be a directory. You can also inside the test add files to the directory, so it's not empty.
Although this works, it has some issues with setting it up and cleaning it up. A better way is to abstract this from the implementation itself and use some kind of provider for it.
A suggestion would be to create an interface where the real implementation returns the real folder and in tests you can mock this.
public interface DirectoryProvider {
public File someDirectory();
}
public class RealDirectoryProvider implements DirectoryProvider {
#Override
public File someDirectory() {
return new File(APP_CONST.DIRECTORY);
}
}
you can now make the getData class depend on this abstraction. You didn't give us the class name, so don't pay attention to that part:
public class Data {
private final DirectoryProvider directoryProvider;
public Data(DirectoryProvider directoryProvider) {
this.directoryProvider = directoryProvider;
}
private Map<String, String> getData() {
File[] files = directoryProvider.someDirectory().listFiles();
if (null == files) {
return Collections.emptyMap();
}
List<String> keysList = getKeyList(files);
return keyMap(APP_CONST.DIRECTORY, keysList);
}
}
Now during the test you can just inject your mocked directory/temp dir.
#ExtendWith(MockitoExtension.class)
class AppConfigTest {
#TempDir
File mockedFile;
#Mock
DirectoryProvider directoryProvider;
#InjectMocks
private AppConfig appConfig;
#Test
void getData() throws Exception {
lenient().when(directoryProvider.someDirectory()).thenReturn(mockedFile);
assertNotNull(appConfig.getData());
}
}
You can also add files to the temp dir if you need. This however should be enough to pass the if I think.
Related
I have following Java configuration class which I need to unit test using JUnit:
public class Config {
private static final String AMQ_CONNECTION_URL_TEMPLATE = "failover:(%s)";
private final String awsAmqUrl;
public Config(String url, Optional<String> amqConnectionOptions, PropertiesManager propertiesManager) {
String urlParameter = propertiesManager.getStringParameter(url);
this.awsAmqUrl = constructAmqConnectionString(urlParameter, amqConnectionOptions);
}
private String constructAmqConnectionString(String urlParameter, Optional<String> connectionOptions) {
if (connectionOptions.isPresent()) {
urlParameter = Stream.of(urlParameter.split(","))
.map(url -> url + "?" + connectionOptions.get())
.collect(Collectors.joining(","));
}
return String.format(AMQ_CONNECTION_URL_TEMPLATE, urlParameter);
}
public ConnectionFactory getConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(awsAmqUrl);
return connectionFactory;
}
}
I am struggling to find an optimal solution for constructAmqConnectionString method unit testing as it's marked as private.
There are 3 scenarios I am trying to cover:
urlParameter - comprises comma separated URLs (url1,url2),
connectionOptions is not empty;
urlParameter - comprises comma
separated URLs (url1,url2), connectionOptions is empty;
urlParameter - comprises single URL (url1), connectionOptions is
not empty.
Current solution is to add a getter into Config class for awsAmqUrl field so that logic of constructor's call can be verified/tested:
public String getAwsAmqUrl() {
return this.awsAmqUrl;
}
Tests itself have following logic:
#Test
public void verifyConstructorWithoutMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.empty();
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1,url2)"),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsMultiBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s,url2?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
#Test
public void verifyConstructorWithMqOptionsSingleBroker() {
when(propertiesManager.getStringParameter(any())).thenReturn("url1");
Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
config = new Config("url1", amqConnectionOptions, propertiesManager);
assertEquals(String.format("failover:(url1?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}
Adding a getter just for Unit testing purposes doesn't feel the right thing to do as it's breaking encapsulation.
Is there a better way to approach testing in such scenario?
The only place that your class uses awsAmqUrl is in the getConnectionFactory method. So it looks like this is the method you'll have to use, to make sure the value of awsAmqUrl is correct. So instead of having a getter for awsAmqUrl, use something like
String storedUrl = objectUnderTest.getConnectionFactory().getBrokerUrl();
and then you can make assertions on that URL.
Sure, it makes your test dependent on the behaviour of ActiveMQConnectionFactory - but that's OK, since your class is tightly coupled to that particular class anyway.
I'm trying to learn some integration testing whilst creating a project to make a game database, and have got a class that calls a service within it's methods
public DigitalGame findDigitalGameByTitleAndPlatform(DigitalGame digitalGame){
if(digitalGame == null){
return null;
}
return GamesJPAService.iDigitalGameRepository.findDigitalGameByTitleAndPlatform(digitalGame.getTitle(), digitalGame.getPlatform());
}
The GamesJpaService as shown below, is initialised as an #Service when the project is started as an application. But is not initialised within the test class, no matter whether it is autowired,newed up, or any other method I have spotted to try and initialise it. for example using the #InjectMocks annotation
#Service
public class GamesJPAService implements IJpaServices {
public static IDigitalGameRepository iDigitalGameRepository;
#Autowired
private IDigitalGameRepository iDigitalGameRepo;
#PostConstruct
public void init(){
iDigitalGameRepository = this.iDigitalGameRepo;
}
}
The IDigitalGameRepository that it calls looks like:
public interface IDigitalGameRepository extends JpaRepository<DigitalGame, String> {
DigitalGame findDigitalGameByTitleAndPlatform(String title, Platform platform);
}
I have ommited some methods from the proceeding code. My test class also looks like (at the moment)
#DisplayName("Digital Games Dao Test")
class DigitalGamesDaoTest {
Set<Peripheral> compatiblePeriphs = new HashSet<>();
Set<SupportedLanguages> supportedLanguages = new HashSet<>();
List<Mods> mods = new ArrayList<>();
List<DLC> dlc = new ArrayList<>();
DigitalGame olliolli = new DigitalGame("olliolli", "", "sports", "olliolli", new ReleaseDate(), new Platform(), compatiblePeriphs, new Developer(), new Publisher(), new Region(), supportedLanguages, new NoneEnglish(), mods, dlc, "", new SpecialEdition(), true, new Platform());
private IDigitalGamesDao digitalGamesDao;
#Mock
GamesJPAService gamesJpaService;
#BeforeEach
void setUp() {
digitalGamesDao = new DigitalGamesDao();
}
#Test
#DisplayName("insertDigitalGame should return false when digital game already in database")
public void insertDigitalGameShouldReturnFalseWhenDigitalGameInDatabase(){
EntityManager mockEntityManager = mock(EntityManager.class);
when(GamesJPAService.iDigitalGameRepository.findDigitalGameByTitleAndPlatform(olliolli.getTitle(), olliolli.getPlatform())).thenReturn(olliolli);
doReturn(olliolli).when(digitalGamesDao.findDigitalGameByTitleAndPlatform(olliolli));
when(digitalGamesDao.findDigitalGameByTitleAndPlatform(olliolli)).thenReturn(olliolli);
when(mockEntityManager.merge(olliolli)).thenReturn(null);
assertFalse(digitalGamesDao.insertDigitalGame(olliolli));
}
#Test
#DisplayName("insertDigitalGame should return true when digital game added to database")
public void insertDigitalGameShouldReturnTrueWhenDigitalGameNotInDatabase(){
}
and I am getting a null pointer exception,
java.lang.NullPointerException: Cannot invoke "com.me.gamedatabasebackend.dao.gamesJPA.IDigitalGameRepository.findDigitalGameByTitleAndPlatform(String, com.me.gamedatabasebackend.model.platform.Platform)" because "com.me.gamedatabasebackend.dao.gamesJPA.GamesJPAService.iDigitalGameRepository" is null
As GamesJPAService is not being tested I need to know how to skip it and just return the value. So I need help to find a way for either, doing a component scan from my test class, or importing the GamesJPAService into it in a useable manner.
You need to annotate your test class with some think like that to make injection works
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = { SpringTestConfiguration.class })
DisplayName("Digital Games Dao Test")
class DigitalGamesDaoTest {
...
But you have a nullpointer because GamesJPAService.iDigitalGameRepository is null. That's a static property and the instance of GamesJPAService is a mock so the init() method is never call.
I have created a factory to provide instance of IMyProcessor based on some boolean flag.
The below populates the map with both of my implementations.
#Component
public class MyProcessorFactory {
private static final Map<String, IMyProcessor> processorServiceCache = new HashMap<>();
#Value("${processor.async:true}")
private boolean isAsync;
public MyProcessorFactory(final List<IMyProcessor> processors) {
for (IMyProcessor service : processors) {
processorServiceCache.put(service.getType(), service);
}
}
public IMyProcessor getInstance() {
IMyProcessor processor = isAsync ? processorServiceCache.get("asynchronous") : processorServiceCache.get("synchronous");
return processor;
}
}
I am now trying to write a Unit test using Junit5 but I am struggling to setup the List of implementations:
I have tried the following:
#ExtendWith(MockitoExtension.class)
class ProcessorFactoryTest {
#InjectMocks
private MyProcessorFactory myProcessorFactory;
#Test
void testAsyncIsReturned() {
}
#Test
void testSyncisReturned() {}
}
I want to test based on the boolean flag async true/false, the correct implementation is returned.
It will be helpful to see how you write such test cases. I autowire the implementations of the interface as construction injection into a list then add to a map using a string key.
Along with answer, I am open to other ideas/refactorings that may make the testing easier.
I am writing unit test case for a Class
public class CurrentMoreInfoDataProvider implements CurrentMoreInfoInterface.presenterToModel{
private CurrentMoreInfoInterface.modelToPresenter modelToPresenter;
public CurrentMoreInfoDataProvider(CurrentMoreInfoInterface.modelToPresenter modelToPresenter) {
this.modelToPresenter = modelToPresenter;
}
#Override
public void provideData() {
WeatherApiResponsePojo apiWeatherData = WeatherDataSingleton.getInstance().getApiWeatherData();
if(null != apiWeatherData.getCurrently()){
CurrentlyPojo currently = apiWeatherData.getCurrently();
if(null != currently){
populateWeatherData(currently);
}
}
}
public void populateWeatherData(CurrentlyPojo currently) {....}
I want to just use verify method of power mock to test whether populateWeatherData get executed or not. Below is my test case so far.
#RunWith(PowerMockRunner.class)
#PrepareForTest(CurrentMoreInfoDataProvider.class)
public class TestCurrentMoreInfoDataProvider {
private CurrentMoreInfoDataProvider dataProvider;
#Mock
CurrentMoreInfoInterface.modelToPresenter modelToPresenter;
private CurrentlyPojo currentlyPojo = new CurrentlyPojo();
#Test
public void testPopulateWeatherData(){
dataProvider = PowerMockito.spy(new CurrentMoreInfoDataProvider(modelToPresenter));
dataProvider.provideData();
Mockito.verify(dataProvider).populateWeatherData(currentlyPojo);
}
}
If I run this I get null pointer exception in provideData method at
if(null != apiWeatherData.getCurrently()){
How should I provide apiWeatherData to provideData method in that class?
You have to mock WeatherDataSingleton.getInstance().getApiWeatherData() too.
This would be much easier if you would not use static access in general and the Singelton pattern in particular.
I tried mocking it, but how should i provide that mock object to provideData() ?
create a mock of WeatherDataSingleton.
Configure your Test so that this mock is used (by properly using dependency injection or by surrendering to your bad design using Powermock).
configure the mock to return the data:
doReturn(currentlyPojo).when(weatherDataSingletonMock).getApiWeatherData();
This resolves the NPE.
I dont think you need to go for PowerMockito if you apply a simple refactor to your production code:
public class CurrentMoreInfoDataProvider{
#Override
public void provideData() {
WeatherApiResponsePojo apiWeatherData = getApiWeatherData();
if(null != apiWeatherData.getCurrently()){
CurrentlyPojo currently = apiWeatherData.getCurrently();
if(null != currently){
populateWeatherData(currently);
}
}
}
WeatherApiResponsePojo getApiWeatherData(){
return WeatherDataSingleton.getInstance().getApiWeatherData();
}
then in your test expect that new method to return certain object:
#RunWith(MockitoJUnitRunner.class)
public class TestCurrentMoreInfoDataProvider {
private CurrentMoreInfoDataProvider dataProvider;
#Mock
CurrentMoreInfoInterface.modelToPresenter modelToPresenter;
#Mock
WeatherApiResponsePojo apiWeatherDataMock;
private CurrentlyPojo currentlyPojo = new CurrentlyPojo();
#Test
public void testPopulateWeatherData(){
dataProvider = PowerMockito.spy(new CurrentMoreInfoDataProvider(modelToPresenter));
doReturn(apiWeatherDataMock).when(dataProvider).getApiWeatherData();
dataProvider.provideData();
Mockito.verify(dataProvider).populateWeatherData(currentlyPojo);
}
}
Consider the following field and method from a class i need to test.
private final static String pathToUUID = "path/to/my/file.txt";
public String getUuid () throws Exception {
return new String(Files.readAllBytes(Paths.get(pathToUUID)));;
}
The UUID is stored in a file that is created on the application's first run. A file.txt exists in the location indicated by pathToUUID. I am trying (and struggling) to write a unit test for this method.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Files.class})
public class MyTest {
private final String expected = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
#Test
public void testGetUuid() throws Exception {
UUIDGetter getter = new UUIDGetter();
PowerMockito.mockStatic(Files.class);
when(Files.readAllBytes(any(Path.class)).thenReturn(expected.getBytes());
String retrieved = getter.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Unfortunately when().thenReturn() is not called during testing and the test performs as an integration test, reading the file from the file system and returning its value, rather simply than the mock value i expect. However, if i spoof a call to Files.readAllBytes() in the test method and echo the result to the console, the expected value displays.
So, how can i get my method under test to properly function with the PowerMock when()-thenReturn() pattern?
For anyone facing a similar problem, i solved this by making the following changes to my test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({UUIDStasher.class})
public class TestUUIDStasher {
private final String expectedUUID = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
Path spoofPath = Paths.get("C:\\DIRECTORY");
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
when(Paths.get(any(String.class))).thenReturn(spoofPath);
when(Files.readAllBytes(any(Path.class))).thenReturn(expectedUUID.getBytes());
}
#Test
public void testGetUUID() throws Exception {
UUIDStasher stasher = new UUIDStasher();
String retrieved = stasher.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Your class that you need to test is written in a bad way. The path shouldn't be hard coded - make it parametrizable - for example inject the path via the constructor. Then, in your integration tests just inject the path to your test resources and you're ready to go. No PowerMock, no hacks - simple constructor injection.
JDK classes are hard to deal with when using PowerMock. Here's what I would do in your case:
Refactor UUIDGetter to add a constructor for testing purposes that accepts the path to the "uuid" file:
package so37059406;
import java.nio.file.Files;
import java.nio.file.Paths;
public class UUIDGetter {
private final static String PATH_TO_UUID = "path/to/my/file.txt";
private final String path;
public UUIDGetter() {
this(PATH_TO_UUID);
}
// for testing purposes
protected UUIDGetter(final String path) {
this.path = path;
}
public String getUuid() throws Exception {
return new String(Files.readAllBytes(Paths.get(this.path)));
}
}
then test it like this:
package so37059406;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UUIDGetterTest {
#Test
public void testGetUuid() throws Exception {
final UUIDGetter getter = new UUIDGetter(getClass().getClassLoader().getResource("so37059406/uuid.txt").getPath());
assertEquals("19dcd640-0da7-4b1a-9048-1575ee9c5e39", getter.getUuid());
}
}
With a resource file (in test resources folder) named "so37059406/uuid.txt" and containing (no end-of-line):
19dcd640-0da7-4b1a-9048-1575ee9c5e39
This is IMHO, way better, because:
No powermock: it's a powerful tool but it comes with a price (slower tests, possible tests strange interactions
It's more readable / easy to understand