I have a JUnit test class, in which I create a TemporaryFolder and test my program in this directory with newly created files and folders. Currently, everything is set up at #Before event:
#Rule
public TemporaryFolder folder = new TemporaryFolder();
File testFile1;
File testFile2;
public List<String> folderItems = new ArrayList<>();
#Before
public void initalise() throws IOException {
testFile1 = folder.newFile("test1.txt");
testFile2 = folder.newFile("test2.txt");
folderItems.add(testFile1.getName());
folderItems.add(testFile2.getName());
Directory.setCurrentDirectory(folder.getRoot().getAbsolutePath());
}
However, I read that #Before is executed before each test and since my tests will all be using the same folder structure, it is better to use #BeforeClass which is only executed once. Here, however, I have an issue with the fact that the method needs to be static. That yields error because then all the variables would need to be static. However, the list can't be static as I am adding the testFiles name into it during initialization.
Any idea how to solve this?
EDIT
I have changed the file into this:
#ClassRule
public static TemporaryFolder folder = new TemporaryFolder();
public static File testFile1;
public static File testFile2;
public static List<String> folderItems;
#BeforeClass
public static void setUpFolder() {
try {
testFile1 = folder.newFile("test1.txt");
} catch (IOException e) {
return;
}
try {
testFile2 = folder.newFile("test2.txt");
} catch (IOException e) {
return;
}
folderItems.add(testFile1.getName());
folderItems.add(testFile2.getName());
Directory.setCurrentDirectory(folder.getRoot().getAbsolutePath());
}
Despite this not throwing any errors, my test doesn't actually get executed and passed, I receive and initializationError
Related
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.
I have a unit test like this, using
org.junit.contrib.java.lang.system.ExpectedSystemExit
org.junit.rules.TemporaryFolder
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
#Test
public void createsFile_integrationTest() throws Exception{
final File tempFolder = folder.newFolder("temp");
exit.expectSystemExitWithStatus(0);
exit.checkAssertionAfterwards(new Assertion() {
#Override
public void checkAssertion() throws Exception {
assertTrue(new File(tempFolder.getAbsolutePath() + "/created-file.txt").exists());
}
main.run(tempFolder.getAbsolutePath() +"/created-file.txt");
});
The problem with this, is that the temporary folder starts tearing down as soon as it gets system exit, and not after my checkAssertion() is called.
Is there a way I can prevent my temporary folder tear down until the end of checkAssertion?
Edit: I think what the answer is - is to do a refactor and separate these to two tests - one where I test system exit, and the other where I test file creation.
You have to enforce an order on the rules so that the ExpectedSystemExit rule can check the assertion before TemporaryFolder is shutdown. You can use JUnit's RuleChain for this.
private final TemporaryFolder folder = new TemporaryFolder();
private final ExpectedSystemExit exit = ExpectedSystemExit.none();
#Rule
public TestRule chain = RuleChain.outerRule(folder).around(exit);
#Test
public void createsFile_integrationTest() throws Exception {
final File tempFolder = folder.newFolder("temp");
exit.expectSystemExitWithStatus(0);
exit.checkAssertionAfterwards(new Assertion() {
#Override
public void checkAssertion() throws Exception {
assertTrue(new File(tempFolder.getAbsolutePath() + "/created-file.txt").exists());
}
});
main.run(tempFolder.getAbsolutePath() +"/created-file.txt");
}
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
My project is not Spring based .Its a java with Hibernate.Building tool - Maven.
I am loading data from one database to HSQL DB before running junits.
My DB util class:
public class DatabaseUtil {
SchemaLoad schemaLoad = new SchemaLoad();
DataLoad dataLoad = new DataLoad();
boolean dataLoaded = false;
static final String filename1 = "test1.txt";
static final String filename2 = "text2.txt";
void dbLoad() throws SQLException {
if (!dataLoaded) {
schemaLoad.cloneSchema(filename1);
dataLoad.exportData(filename2);
System.out.println("***********executed**********8");
dataLoaded = true;
}
}
}
First Test Case:
public class TestCase {
TrainRepository trainRepository = new TrainRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForTrainRepo() throws Exception {
//TestCases
}
Second Test case:
public class TestCase1 {
AirRepository airRepository = new AirRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForAirRepo() throws Exception {
//TestCases
}
Both the test cases are running fine.But Its executing databaseUtil.dbLoad(); method on each junit.
My question is I need to load the database only once ie before start of first junit and need to set some indicator .The further junits need to check the DB instance If DB instance is there it should not load the data ie DatabaseUtil class need to be singleton.
All the junits are running through maven suffire plugin during mvn install phase.
Kindly help me to achieve this.
void dbLoad() will be called each time.
then use a static variable to keep track
static boolean dataLoaded = false;
if you don't use spring you need to implement caching yourself. you have a few option. use static field with some kind of synchronization (in case you use/plan to use threads). other option is to switch to testng that gives you #BeforeGroup functionality so you can mark all your db tests and have your initialization run before.
I am trying to test something like this:
try {
logger.info("message");
//do something
} catch(Exception e) {
logger.error(errorMessage);
}
I know that it's not a good practice to catch an Exception, but there is some legacy code and there is no time for refactoring.
So, I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
Please notice that I can`t use:
final Logger logger = LogManager.getLogger(AnaliticsService.class);
final Appender mockAppender = mock(Appender.class);
logger.addAppender(mockAppender);
final ArgumentCaptor<LoggingEvent> captor = ArgumentCaptor.forClass(LoggingEvent.class);
Log4jConfigHelper.getInstance().bufferConfiguration();
verify(mockAppender, times(x)).doAppend(captor.capture());
because I don`t know how many messages are logged when UT is running.
You should try to make a Mock for LoggerFactory.
First annotate your TestClass with:
#RunWith(PowerMockRunner.class)
#PrepareForTest({YourController.class, LoggerFactory.class})
Then make a test, which calls needed method and veryfies errors:
#Test
public void testErrorLogging() throws Exception {
mockStatic(LoggerFactory.class);
Logger logger = mock(Logger.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(logger);
YourController controller = new YourController();
controller.someMethod();
verify(logger).error(anyString());
}
Log messages are part of the user interface of your code. Code that does computations should not make assumptions about the manner in which log messages are made available to the user, the text and language of the log messages, or even whether messages are communicated as text (rather than, say, a graphical means). So computational code should delegate to an associated logger class (in the UI/presentation layer) that hides all those details.
If the computational code only requires that the associated logger conforms to an interface, and uses dependency injection for being associated with a logger class, it is easy to mock the logger to examine whether the computational code has requested logging.
So if the code to be tested is like this::
public class MyService
{
private final MyServiceLogger logger;
MyService(MyServiceLogger logger)
{
this.logger = Objects.requireNonNull(logger);
}
public void processFile(Path path) {
...
try{
...
} catch (EOFException e) {
logger.logUnexpectedEOF(path);
}
}
}
public interface MyServiceLogger
{
public logUnexpectedEOF(Path path);
}
public class MyServiceTextLogger implements MyServiceLogger
{
private final Logger textLogger = LogManager.getLogger(MyService.class);;
#Override
public logUnexpectedEOF(Path path) {
textLogger.error("unexpected EOF for file {}",path);
}
}
You can test it like this:
public class MyServiceTest
{
private static class MockMyServiceLogger implements MyServiceLogger
{
private Path path;
private int nCalls_logUnexpectedEOF;
#Override
public logUnexpectedEOF(Path path) {
++nCalls_logUnexpectedEOF;
this.path = path;
}
void assertCalled_logUnexpectedEOF(int nCalls, Path path) {
assertEquals("Called logUnexpectedEOF, nCalls", nCalls, nCalls_logUnexpectedEOF);
assertEquals("Called logUnexpectedEOF, path", path, this.path);
}
}
#Test
public void processFile_unexpectedEOF() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(1, testPath);
}
#Test
public void processFile_OK() {
Path testPath = ...
...
MockMyServiceLogger mockLogger = new MockMyServiceLogger();
MyService service = new MyService(mockLogger);
service.processFile(testPath);
mockLogger.assertCalled_logUnexpectedEOF(0, null);
}
}
I write an unit test so that a NullPointerException will be thrown inside try block, but now I don't know how to write the assert line(obviously, unit test have to fail all the time).
You don't need to check for an exception this way. A test which throws an Exception fails.
} catch(Exception e) {
logger.error(errorMessage, e);
throw e; // report the error to the test
}
Note: when to throw an error to the testing framework it will log/print it so I suspect you don't need to be catching it in the first place.