How to Mock file array using PowerMockito? - java

I have the below static method in my Configs class which I am trying to Mock using Powermockito.
Ln 1 public static method getConfigs(){
2
3 File[] files = new File(/tmp/dir/).listFiles();
4 if (null == files) {
5 return Collections.emptyMap();
6 }
7 return getData();
}
And in my test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(Configs.class)
public class ConfigsTest {
#InjectMocks
Configs configs;
#Test
public void testGetConfigs() throws Exception {
PowerMockito.mockStatic(Configs.class);
Map<String, Object> map = new HashMap<>();
map.put("data", "data");
File file = PowerMockito.mock(File.class);
File[] files = new File[] { new File("file") };
PowerMockito.when(file.listFiles()).thenReturn(files);
PowerMockito.when(configs.getDataMap()).thenReturn(map);
Map data = secrets.getDataMap();
Assert.assertNotNull(data);
}
Now, the issue is that the test fails at the IF condition, line 4. I tried several ways to mock and add value to the file object used in the null check. I need my tests to cover beyond the IF condition to meet sonar coverage. Please let me know how I could achieve this.

I think it should be like this
PowerMockito.mockStatic(Configs.class);
PowerMockito.when(Configs.getConfigs()).thenReturn(map);
powermock-static

Related

How to remove custome properties from SOAPUI test case using java?

I have some custom properties for all the test cases in SoapUI.
I am able to delete using Groovy script step as described in below question:
How to remove Custom Properties from a SoapUI TestCase using Groovy?
testRunner.testCase.removeProperty( "Testcase_Property" );
But I wanted to delete these properties from JAVA. Below is the code I wrote:
String soapuiProjectPath = "ProjectLocation";
WsdlProject project = new WsdlProject(soapuiProjectPath);
StringToObjectMap context = new StringToObjectMap();
TestSuite testSuite = project.getTestSuiteByName("TestSuiteName");
WsdlTestSuite wsdlSuite = (WsdlTestSuite) testSuite;
List<TestCase> allTestCaseList = wsdlSuite.getTestCaseList();
for (TestCase testCase : allTestCaseList) {
WsdlTestCaseRunner testCaseRunner = new WsdlTestCaseRunner((WsdlTestCase) testCase, context);
List<TestProperty> testCasePropertyList = testCase.getPropertyList();
for (TestProperty testProperty : testCasePropertyList) {
WsdlTestRunContext runContext = testCaseRunner.getRunContext();
runContext.removeProperty(testProperty.getName());
}
}
System.out.println("Completed execution.");
project.save();
It is not throwing any exception. But not actually removing the custom properties as well.
Because you've to apply the removeProperty in WsdlTestCase not in WsdlTestRunContext. You can change your testCase loop code for something like:
for(TestCase testCase : allTestCaseList) {
List<TestProperty> testCasePropertyList = testCase.getPropertyList();
for (TestProperty testProperty : testCasePropertyList) {
((WsdlTestCase) testCase).removeProperty(testProperty.getName());
}
}
Hope it helps,

How do I include a resource file in Java project to be used with just new File()?

I'm writing a UDF for Pig using Java. It works fine but Pig doesn't give me options to separate environment. What my Pig script is doing is to get Geo location from IP address.
Here's my code on the Geo location part.
private static final String GEO_DB = "GeoLite2-City.mmdb";
private static final String GEO_FILE = "/geo/" + GEO_DB;
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
DatabaseReader reader = new DatabaseReader.Builder(new File(GEO_DB)).build();
// other stuff
}
GeoLite2-City.mmdb exists in HDFS that's why I can refer from absolute path using /geo/GeoLite2-City.mmdb.
However, I can't do that from my JUnit test or I have to create /geo/GeoLite2-City.mmdb on my local machine and Jenkins which is not ideal. I'm trying to figure out a way to make my test passed while using new File(GEO_DB) and not
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb') because
getClass().getResourceAsStream('./geo/GeoLite2-City.mmdb')
Doesn't work in Hadoop.
And if I run Junit test it would fail because I don't have /geo/GeoLite2-City.mmdb on my local machine.
Is there anyway I can overcome this? I just want my tests to pass without changing the code to be using getClass().getResourceAsStream and I can't if/else around that because Pig doesn't give me a way to pass in parameter or maybe I'm missing something.
And this is my JUnit test
#Test
#Ignore
public void shouldGetGeoData() throws Exception {
String ipTest = "128.101.101.101";
Map<String, Object> geoJson = new LogLine2Json().geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
which it works if I read the database file from resource folder. That's why I have #Ignore
Besides, your whole code looks pretty un-testable.
Every time when you directly call new in your production code, you prevent dependency injection; and thereby you make it much harder to test your code.
The point is to not call new File() within your production code.
Instead, you could use a factory that gives you a "ready to use" DatabaseReader object. Then you can test your factory to do the right thing; and you can mock that factory when testing this code (to return a mocked database reader).
So, that one file instance is just the top of your "testing problems" here.
Honestly: don't write production code first. Do TDD: write test cases first; and you will quickly learn that such production code that you are presenting here is really hard to test. And when you apply TDD, you start from "test perspective", and you will create production code that is really testable.
You have to make the file location configurable. E.g. inject it via constructor. E.g. you could create a non-default constructor for testing only.
public class LogLine2Json {
private static final String DEFAULT_GEO_DB = "GeoLite2-City.mmdb";
private static final String DEFAULT_GEO_FILE = "/geo/" + GEO_DB;
private final String geoFile;
public LogLine2Json() {
this(DEFAULT_GEO_FILE);
}
LogLine2Json(String geoFile) {
this.geoFile = geoFile;
}
public Map<String, Object> geoData(String ipStr) {
Map<String, Object> geoMap = new HashMap<String, Object>();
File file = new File(geoFile);
DatabaseReader reader = new DatabaseReader.Builder(file).build();
// other stuff
}
}
Now you can create a file from the resource and use this file in your test.
public class LogLine2JsonTest {
#Rule
public final TemporaryFolder folder = new TemporaryFolder();
#Test
public void shouldGetGeoData() throws Exception {
File dbFile = copyResourceToFile("/geo/GeoLite2-City.mmdb");
String ipTest = "128.101.101.101";
LogLine2Json logLine2Json = new LogLine2Json(dbFile.getAbsolutePath())
Map<String, Object> geoJson = logLine2Json.geoData(ipTest);
assertThat(geoJson.get("lLa").toString(), is(equalTo("44.9759")));
assertThat(geoJson.get("lLo").toString(), is(equalTo("-93.2166")));
}
private File copyResourceToFile(String name) throws IOException {
InputStream resource = getClass().getResourceAsStream(name);
File file = folder.newFile();
Files.copy(resource, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
return file;
}
}
TemporaryFolder is a JUnit rule that deletes every file that is created during test afterwards.
You may modify the asserts by using the hasToString matcher. This will give you more detailed information in case of a failing test. (And you have to read/write less code.)
assertThat(geoJson.get("lLa"), hasToString("44.9759"));
assertThat(geoJson.get("lLo"), hasToString("-93.2166"));
You don't. Your question embodies a contradiction in terms. Resources are not files and do not live in the file system. You can either distribute the file separately from the JAR and use it as a File or include it in the JAR and use it as a resource. Not both. You have to make up your mind.

How to unit test and mock a method that takes a file as a parameter

I have a class CollectionObject which creates a ArrayList.
public class CollectionObject {
private List<String> collectionObject;
public CollectionObject() {
collectionObject = new ArrayList<String>();
}
public List<String> getCollectionObject() {
return collectionObject;
}
public void add(final String stringToWrite) throws VerifyException {
collectionObject.add(stringToWrite);
}
}
There is another class which takes in the class CollectionObject and uses it to write the contents of the file to the class CollectionObject.
public class ReaderFileWriterObjectService {
private BufferedReader bufferedReader;
private CollectionObject collectionObject;
private String line;
public CollectionObject getCollectionObjectAfterWritingFromAFile(final File file)
throws VerifyException, IOException {
collectionObject = new CollectionObject();
bufferedReader = new BufferedReader(new FileReader(file));
while ((line = bufferedReader.readLine()) != null) {
collectionObject.add(line);
}
bufferedReader.close();
return collectionObject;
}
How to Test and Mock the method of the class ReaderFileWriterObjectService?
Let me complement on #LouisWasserman's answer.
You just cannot test APIs which rely on java.io.File; this class cannot be reliably unit tested (even though it is not even final at the JDK level).
But this is not the case with the new filesystem API, which appeared with Java 7.
Also known as JSR 203, this API provides a unified API to any storage medium providing "filesystem objects".
Short story:
a "filesystem object" is materialized by a Path in this API;
any JDK implementing JSR 203 (ie, any Java 7+ version) supports this API;
to get a Path from a resource on the default FileSystem, you can use Paths.get();
but you are not limited to that.
In short, in your API and test case, you should use Path, not File. And if you want to test anything related to some filesystem resource, use the JDK's Files class to test Path instances.
And you can create FileSystems out of your main, disk based, file system. Recommendation: use this.
I am doing the same thing, And the following idea is working,
I hope this will work for u too,
#InjectMocks
private CollectionObject collectionObject;
#Test
public void getCollectionObjectAfterWritingFromAFile() throws Exception {
CollectionObject expectedObject =new CollectionObject();
List<String> expectedList=new ArrayList<String>();
expectedList.add("100");
CollectionObject resultObject =new CollectionObject();
BufferedReader reader=new BufferedReader(new StringReader("100"));
PowerMockito.mock(BufferedReader.class);
PowerMockito.mock(FileReader.class);
PowerMockito.whenNew(FileReader.class).withArguments("test10.csv").thenReturn(null);
PowerMockito.whenNew(BufferedReader.class).withArguments(null).thenReturn(reader);
resultObject=collectionObject.getCollectionObjectAfterWritingFromAFile( "test10.csv");
assertEquals(expectedObject ,resultObject );
}
You can use JUnit's TemporaryFolder for creating a file and copy the contents from a resource to it.
public YourText {
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Test
public void checkSomething() throws Exception {
InputStream resource = getClass().getResourceAsStream("/your/resource");
File file = folder.newFile();
Files.copy(resource, file);
ReaderFileWriterObjectService service = ...
CollectionObject collection = service
.getCollectionObjectAfterWritingFromAFile(file);
...
}
You cannot. You're pretty much out of luck. A better design would accept a Java 7 java.nio.file.FileSystem and a Path that could be swapped out for a test implementation, e.g. https://github.com/google/jimfs.
Okay, at first lets consider what do u want to test? If it is unit test then u dont want to test integrations like communications with filesystem, you have to test your own logic and your logic is something like:
1) Read next line from file using file system integration
2) add this line into my object
The second step you should not test because this method is too easy to break. The first step you can't test cause it performs integrations call. So i don't think that here u need a unit test
But if your logic will be more complicated, then you can introduce interface wrapper and mock it in your test:
public interface FileWrapper{
public String readLine();
public void close();
}
public class FileWrapperImpl implements FileWrapper{
private File file;
private BufferedReader reader;
public FileWrapperImpl (File file){
this.file = file;
this.reader= ...
}
public String readLine(){
return reader.nextLine();
}
}
And then your ReaderFileWriterObjectService:
public CollectionObject getCollectionObjectAfterWritingFromAFile(FileWrapper wrapper)
CollectionObject collectionObject = new CollectionObject();
while ((line = wrapper.readLine()) != null) {
collectionObject.add(line);
}
wrapper.close();
return collectionObject;
}
And now you can easily mock FileWrapper for test and pass it to your service
I'd suggest changing the API to accept Reader or BufferedReader - those can be mocked. Hide the dependency on file with a factory.

Mule - testing a flow that has an inbound/outbound endpoint with a Mule property as the file path

I'm trying to write a test for a Mule flow that will involve dropping a file in a location, waiting for it to be processed by my flow and compare the output to see if it has been transformed correctly. My flow looks as follows:
<flow name="mainFlow" processingStrategy="synchronous">
<file:inbound-endpoint name="fileIn" path="${inboundPath}">
<file:filename-regex-filter pattern="myFile.csv" caseSensitive="true"/>
</file:inbound-endpoint>
...
<file:outbound-endpoint path="${outboundPath}" outputPattern="out.csv"/>
</flow>
Is there a way I can access the inboundPath and outboundPath Mule properties inside of my test class so that I can drop files and wait for output in the correct places?
The test class I'm using is:
public class MappingTest extends BaseFileToFileFunctionalTest {
#Override
protected String getConfigResources() {
return "mappingtest.xml";
}
#Test
public void testMapping() throws Exception {
dropInputFileIntoPlace("myFile.csv");
waitForOutputFile("out.csv", 100);
assertEquals(getExpectedOutputFile("expected-out.csv"), getActualOutputFile("out.csv"));
}
}
Which extends this class:
public abstract class BaseFileToFileFunctionalTest extends FunctionalTestCase {
private static final File INPUT_DIR = new File("/tmp/muletest/input");
private static final File OUTPUT_DIR = new File("/tmp/muletest/output");
private static final Charset CHARSET = Charsets.UTF_8;
#Before
public void setup() {
new File("/tmp/muletest/input").mkdirs();
new File("/tmp/muletest/output").mkdirs();
empty(INPUT_DIR);
empty(OUTPUT_DIR);
}
private void empty(File inputDir) {
for (File file : inputDir.listFiles()) {
file.delete();
}
}
protected File waitForOutputFile(String expectedFileName, int retryAttempts) throws InterruptedException {
boolean polling = true;
int attemptsRemaining = retryAttempts;
File outputFile = new File(OUTPUT_DIR, expectedFileName);
while (polling) {
Thread.sleep(100L);
if (outputFile.exists()) {
polling = false;
}
if (attemptsRemaining == 0) {
VisibleAssertions.fail("Output file did not appear within expected time");
}
attemptsRemaining--;
}
outputFile.deleteOnExit();
return outputFile;
}
protected void dropInputFileIntoPlace(String inputFileResourceName) throws IOException {
File inputFile = new File(INPUT_DIR, inputFileResourceName);
Files.copy(Resources.newInputStreamSupplier(Resources.getResource(inputFileResourceName)), inputFile);
inputFile.deleteOnExit();
}
protected String getActualOutputFile(String outputFileName) throws IOException {
File outputFile = new File(OUTPUT_DIR, outputFileName);
return Files.toString(outputFile, CHARSET);
}
protected String getExpectedOutputFile(String resourceName) throws IOException {
return Resources.toString(Resources.getResource(resourceName), CHARSET);
}
}
As you can see I'm currently creating temporary input/output directories. I'd like to make this part read from the Mule properties if possible? Thanks in advance.
After observing your test classes and code I could see that you want to dynamically create temp folders place files in them. And the flow should read the files from Temp Directory and write output to another Temp directory. Point to be noted is that Mule's Endpoints are created when the configuration is loaded. So the ${inbound} and ${outbound} should be provided to the mule flow by the time they are provided.
So one option can be to create a dummy flow pointing to the temp folders for testing.
or
Create a test properties file pointing to the temp folders and load that to your flow config, so that your flow endpoints will get the temp folder paths.
In any way path cannot be provided to the flow inbound endpoints after they have been created(on config load).
Update1:
As per your comment the solution with option would be like the following.
Seperate the properties loading part of the config into another config.
Like "mapping-core-config.xml,mappingtest.xml" where the mapping-core-config will have the tags to load the properties file.
Now create a test config file for the mapping-core-config.xml file which loads the test properties file. This should be used in your test config. This way without modifying or disturbing your main code, you can test your flows pointing to temp folders.
"mapping-core-test-config.xml,mappingtest.xml"
Note: The test config can reside in the src/test/resources folders.
Hope this helps.

JUnit : When do I declare a TemporaryFolder object?

I am trying to test a method that copies a source file to a dest file using JUnit's TemporaryFolder. I get a Java IOException when I try run this test however. Does it matter where I make the declaration for the folder? (My test class has several different tests in it). And if so, what is the proper way to do it? I ask because I currently have several unit tests above this code, then I try to set up the testing for the file copying. Maybe the #Rule-#Before-#Test block needs to be in its own class? Here is the snippet where I have coded the test:
...other tests...then:
#Rule
public static TemporaryFolder tmp = new TemporaryFolder();
private File f1, f2;
#Before
public void createTestData() throws IOException {
f1 = tmp.newFile("src.txt");
f2 = tmp.newFile("dest.txt");
BufferedWriter out = new BufferedWriter(new FileWriter(f1));
out.write("This should generate some \n" +
"test data that will be used in \n" +
"the following method.");
out.close();
}
#Test
public void copyFileTest() {
out.println("file 1 length: " + f1.length());
try {
copyFile(f1, f2);
} catch (IOException e) {
e.getMessage();
e.printStackTrace();
}
if (f1.length() != f2.length())
fail();
else if (!f1.equals(f2))
fail();
assertSame(f1, f2);
}
When I run this test class, all 11 of my tests now fail (which previously passed) and I get java.io.IOException: No such file or directory.
So looking at the JUnit Javadoc, I have found out that any declaration under #Rule must be public, and not static. So I took out the static and just have:
#Rule
public TemporaryFolder tmp = new TemporaryFolder();
I still do not know for sure if it matters where this declaration is made when you have other unit tests in your class that do not use the #Rule declaration, but this did allow me to run through my tests successfully.
If you really want to declare TemporaryFolder as static, you can use #ClassRule which is used to annotate static fields that contains Rule.
#ClassRule
public static TemporaryFolder tmp = new TemporaryFolder();
Reference: http://junit-team.github.io/junit/javadoc/4.10/org/junit/ClassRule.html

Categories

Resources