I have the following JAVA code:
#Override
public void myFunc(...) {
String content = createFileContent(...);
File f = createFile(content);
uploadFile(f);
deleteFile(f)
}
I want to test to file content, or the file itself (both can work), using unit test.
The file is deleted before the function ends.
What is the approach you would recommend on taking here? (I am using Mockito as mocking framework)
Spy the target test class and define the behavior and verify the calls of the methods, and if you want to assert the argument you can use ArgumentCaptor
public class TargetTest {
#Test
public void shouldDoSomething() {
// Arrange
final String content = "content-file";
final File file = new File();
Target target = spy(target);
doReturn(content).when(target).createFileContent();
doReturn(file).when(target).createFile(content);
doNothing().when(target).uploadFile(file);
doNothing().when(target).deleteFile(file);
// Act
target.myFunc();
// Assert
verify(target).createFileContent();
verify(target).createFile(content);
verify(target).uploadFile(file);
verify(target).deleteFile(file);
}
}
Related
I have the following method
public static File getInventoryFileFromProperties(){
String filePath = getProperty(ConfigProperties.MY_INVENTORY_FILE);
logger.debug("Looking for inventory file at {}", filePath);
return new File(filePath);
}
How do i unit test this for the following condition, ConfigProperties.MY_INVENTORY_FILE is not present in the properties file.
getProperty() // gets values from property file
ConfigProperties.MY_INVENTORY_FILE // is an enum of keys
The best way to make code that accesses external resources -- such as the file system -- unit testable, is by creating an abstraction layer, e.g.:
public class FileAccessor {
public String getProperty(ConfigProperties property) {
// property file access code goes here
}
public File createFile(String filePath) {
return new File(filePath);
}
}
Then, the class-under-test can be refactored to use the resource-accessor through constructor injection of the dependency:
public class ContainingClass {
private FileAccessor fileAccessor;
// this constructor is accessible for the unit tests
ContainingClass(FileAccessor fileAccessor) {
this.fileAccessor = fileAccessor;
}
// this constructor is used by normal client code
public ContainingClass() {
this(new FileAccessor());
}
public File getInventoryFileFromProperties(){
String filePath = fileAccessor.getProperty(ConfigProperties.MY_INVENTORY_FILE);
return fileAccessor.createFile(filePath);
}
}
Finally, unit testing becomes simpler now that you can mock the file access. This test uses the Mockito mocking framework for mocking the dependency, and also works with earlier versions of JUnit:
import static org.mockito.Mockito.*;
import org.junit.Test;
public class ContainingClassTest {
#Test
public void getInventoryFileFromProperties_MY_INVENTORY_FILE_isMissing() {
FileAccessor fileAccessor = mock(FileAccessor.class);
// arrange the config file to return a null value for the property
when(fileAccessor.getProperty(ConfigProperties.MY_INVENTORY_FILE)).thenReturn(null);
// act; call the method
new ContainingClass(fileAccessor).getInventoryFileFromProperties();
// assert that the file-creating method was called with a null path
verify(fileAccessor).createFile(isNull(String.class));
}
}
I have a block of code for which I need to test .Lets say
Class MainClass{
public void startProcess() {
----Some Logic to generate fileName;
uploadFile(fileName);
}
private static void uploadFile(String key) {
fileUpload();
deleteFile();
}
}
I want to write a JUNIT test which will call startProcess but either skip the uploadFile line or just ignore any lines present in uploadFile method .
I tried to use powerMock but it doesnt work . Below is my code
#RunWith(PowerMockRunner.class)
#PrepareForTest(MainClass.class)
public class MainClassTest {
#Test
public void teststartProcess() throws Exception {
processor=PowerMock.createPartialMock(MainClass.class,"uploadFile");
PowerMock.expectPrivate(processor , "uploadFile", "xyz").andAnswer(
new IAnswer<Void>() {
#Override
public Void answer() throws Throwable {
System.out.println("Invoked!");
return null;
}
}).atLeastOnce();
}
}
But it does't override the method uploadFile to just print invoked . It calls fileUpload and deleteFile instead of just skipping the lines and print invoke .
My basic goal is to mock the method uploadFile to just print
private void uploadFile(String key) {
System.out.println("Invoked");
}
I know it's possible using Mockito but we can use either PowerMock or EasyMock .
Instead of trying to go the way with over complicated tests you should consider to refactor your code so that it is testable. From what you provided I would move the file functionality to another class.
something like this:
public class MainClass {
private final FileUploader fileUploader;
public MainClass(FileUploader fileUploader) {
this.fileUploader= fileUploader;
}
public void startProcess() {
fileUploader.uploadFile(fileName);
}
}
With this refactoring you gain the possibility to use plain mocking for the test:
String fileName = "foo";
FileUploader fileUploader = mock(FileUploader.class);
MainClass classUnderTest = new MainClass(fileUploader);
classUnderTest.startProcess();
verify(fileUploader, times(1)).uploadFile(fileName);
An additional benefit is that testing the fileUploader becomes also easy. As you can see I also got rid of all the complicated Partial mocking and private testing.
You have to change the mock object to replay mode before calling the method under test and you have to tell Powermock to work on a static method:
mockStaticStrict(MainClass.class,method(MainClass.class, "uploadFile"));
PowerMock.replay(processor);
processor.startProcess();
See Mocking private methods for further information on this.
I highly suggest to refactor this code and make it testable.
I have the following class(and method in it)
class Fetcher{
public void fetch(String key){
File file = File.createTempFile(key,"*.txt");
.....
....
}
}
I want to unit test this method and want to mock the createTempFile method
For this i have written the unit test as follows
#RunWith(PowerMockRunner.class)
#PrepareForTest({File.class})
public class FetcherTest {
public void test() {
String key = "key";
File file = new File("Hello");
PowerMock.mockStatic(File.class);
EasyMock.expect(File.createTempFile(EasyMock.anyObject(String.class),EasyMock.anyObject(String.class))).andReturn(file).once();
PowerMock.replay(File.class);
Fetcher fetcher = new Fetcher();
fetcher.fetch("key");
PowerMock.verify(File.class);
}
}
Executing the unit test provides the following error:
Expectation failure on verify: File.createTempFile(,):
expected: 1,actual: 0
I have looked through a lot of articles but am not able to figure out what's missing here and why File is not getting mocked. Please help with any suggestions
When you mock Java System classes (and the File is a Java System Class) you have to add a ClassThatCallsTheSystemClass to #PrepareForTest.
So you need to add class Fetcher to #PrepareForTest
#RunWith(PowerMockRunner.class)
#PrepareForTest({Fetcher.class})
I am trying to get a string from a TestNG annotation #Test(groups="Foo") and then use this as a name for a folder I am dynamically generating.
How do I get the text "Foo" from the TestNG annotation so I can use it?
I think a simpler solution to reading the attribute of the annotation (which would involve reflection and friends) would be to use the same constant String:
private static final String FOLDER = "Foo";
#Test(groups = FOLDER)
public void test() {
//create the folder named FOLDER
}
You can get the annotation from a Method (which you can get from the Class.get{,Declared}Methods() method):
Test test = method.getAnnotation(Test.class);
This will be non-null if the annotation was present, and null if it was not. If it is non-null, you can then just call the groups() method on test:
String groups = test.groups();
Why not using a #BeforeMethod method?
#BeforeMethod
public void generateFolderFromGroups(Method m) {
Test test = m.getAnnotation(Test.class);
String[] groups = test.groups();
// generate folder from groups
}
#Test(groups = "Foo")
public void test() {
// the Foo folder will be already created
}
I want to create 2 JUnit TestSuites. They both utilize the same test classes, but they should each use different parameters. For example, in test suite A, I want my data to be collected from file A and to be written to database A. In test suite B, I want my data to be collected from file B and to be written to databaseB.
The reason I use testSuites for this is because:
I can put all the specific parameters in the testsuite classes
I can reuse the testclasses
I can choose which testsuite to run. I do not want all tests to always run with all possible paramaters!
The problem is I cannot really pass the parameters. I understand the way the Parameterized class works with JUnit, but it does not allow point 3 in the list above. If I use the code below it will run my test class with both databse connections, which is not what I want to achieve.
#RunWith(value = Parameterized.class)
public class TestCheckData
{
private File file;
private DatabaseSource databaseSource;
public TestCheckData(File file, DatabaseSource databaseSource)
{
this.file = file;
this.databaseSource = databaseSource;
}
#Parameters
public static Iterable<Object[]> data1()
{
return Arrays.asList(new Object[][]
{
{ TestSuiteA.DATA_FILE_A, TestSuite1.DATABASE_A },
{ TestSuiteB.DATA_FILE_B, TestSuite1.DATABASE_B }
});
}
I already find some way of passing configurations in a spring context in this question, but I'm not using any special framework.
Well, this would be a little unconventional, but you could add a different Test class to the beginning of each suite run that would set the parameters you want to use for that test. So you'd have classes like:
public abstract class StaticParameters {
public static File dataFileToUse = null;
public static DatabaseSource databaseToUse = null;
}
public class Suite1Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_A;
databaseToUse = DATABASE_A;
}
}
public class Suite2Params extends StaticParameters {
#BeforeClass
public static void setParams() {
dataFileToUse = DATA_FILE_B;
databaseToUse = DATABASE_B;
}
}
Then you'd just make Suite1Params or Suite2Params the first in your suite list. You might have to add a fake #Test entry to the params classes, I'm not sure if the Suite runner requires that.
You could modify the tests so that they get the parameters from a config file. This way you would always only have 1 Suite.
The path of the config file can be looked up via a System property.
Then on the invocation of the test suite, you could pass in a different config file by changing the property using the -D option on the JVM.
So for example if you named the proprerty env.properties then your command would be:
%java -Denv.properties=prod.config runMyTests
or
%java -Denv.properties=dev.config runMyTests
etc