Writing Unit test with Mockito with Jgit - java

I am new to Mockito/PowerMockito(as it is a static method), while testing my code. I am not able to write test for this method as it contain fileList along with Jgit method, Can anyone show how can I perform testing for this perticular method.
public static String addAndCommitUntrackedChanges(final File gitFile, final String branchName,
final String commitMessage, List<String> filesList)
throws IOException, GitAPIException {
final Git openedRepo = Git.open(gitFile);
openedRepo.checkout().setCreateBranch(true).setName(branchName).call();
AddCommand add = openedRepo.add();
for (final String file: filesList) {
Path filepath = Paths.get(file); //file absolute Path
final Path repoBasePath = Paths.get("/", "tmp", "PackageName"); //file base common path
final Path relative = repoBasePath.relativize(filepath); //Remove the repoBasePath from filpath
add.addFilepattern(relative.toString());
}
add.call();
// Create a new commit.
RevCommit commit = openedRepo.commit()
.setMessage(commitMessage)
.call();
//Return the Latest Commit_Id
return ObjectId.toString(commit.getId());
}
Thanks in advance!

You should avoid static methods. How are you going to test clients of addAndCommitUntrackedChanges if you can't mock it?
To make addAndCommitUntrackedChanges more testable, introduce a GitWrapper interface:
interface GitWrapper {
Git open(File f);
}
with an implementation:
class DefaultGitWrapper implements GitWrapper {
public Git open(File f) {
return Git.open(f);
}
}
and change the signature of your method to:
public static String addAndCommitUntrackedChanges(
GitWrapper gitWrapper,
final File gitFile,
final String branchName,
final String commitMessage,
List<String> filesList)
and use GitWrapper instead of the static instance of Git.
In the particular case of Git the wrapper isn't needed, because you can just pass an instance of Git, which can be mocked normally, but when you really have a third party class which only provides static methods it's a necessary solution.
Then you can mock the things you need to mock to write a unit test, which would look something like (this is uncompiled code):
class TestAddAndCommitUntrackedChanges {
#Mock
GitWrapper gitWrapper;
#Mock
Git git;
#Mock
CheckoutCommand checkoutCommand;
#Mock
AddCommand addCommand;
#Test
public void testBehaviour() {
List<String> files = List.of("/tmp/PackageName/foo", "/tmp/PackageName/bar");
File gitFile = new File("gitFile");
when(gitWrapper.open(gitFile)).thenReturn(git);
when(git.checkout()).thenReturn(checkoutCommand);
when(checkoutCommand.setName("theBranch"))
.thenReturn(checkoutCommand);
when(git.add()).thenReturn(addCommand);
assertEquals(
"thecommitid",
addAndCommitUntrackedChanges(
gitWrapper, gitFile, "theBranch",
"the commit message", files)
);
verify(checkoutCommand).call();
verify(addCommand).addFilePattern("foo");
verify(addCommand).addFilePattern("bar");
verify(addCommand).call();
}
}
You'll also need to mock and verify the CommitCommand.

Related

Unable to mock certain final classes with PowerMockito - java.lang.IllegalAccessError

When I try to mock certain final classes like SearchGoogleAdsRequest from a certain Google library they give me IllegalAccessError , I have added #PrepareForTest annotation on top of the class.
#RunWith(PowerMockRunner.class)
#PrepareForTest({SearchGoogleAdsRequest.class})
#PowerMockIgnore({"javax.net.ssl.*"})
public class GoogleAdsReportDownloaderTest {
final SearchGoogleAdsRequest searchGoogleAdsRequest = PowerMockito.mock(SearchGoogleAdsRequest.class);
final GoogleAdsServiceClient mockGoogleAdsServiceClient = mock(GoogleAdsServiceClient.class);
final GoogleAdsServiceClient.SearchPagedResponse searchPagedResponse =
mock(GoogleAdsServiceClient.SearchPagedResponse.class);
when(mockGoogleAdsServiceClient.searchPagedCallable()).thenReturn(callable);
when(mockGoogleAdsServiceClient.searchPagedCallable().call(searchGoogleAdsRequest)).thenReturn(searchPagedResponse);
when(searchPagedResponse.iterateAll()).thenReturn(Arrays.asList(mockGoogleAdsRow));
when(mockGoogleAdsServiceClient.search(any())).thenReturn(searchPagedResponse);
Error
java.lang.IllegalAccessError: Class com/google/ads/googleads/v6/services/SearchGoogleAdsRequest$MockitoMock$1353664588 illegally accessing "package private" member of class com/google/protobuf/GeneratedMessageV3$UnusedPrivateParameter
SearchGoogleAdsRequest final class looks like this, which PowerMockito isn't able to mock.
public final class SearchGoogleAdsRequest extends GeneratedMessageV3 implements SearchGoogleAdsRequestOrBuilder {
private static final SearchGoogleAdsRequest DEFAULT_INSTANCE = new SearchGoogleAdsRequest();
private static final Parser<SearchGoogleAdsRequest> PARSER = new AbstractParser<SearchGoogleAdsRequest>() {
public SearchGoogleAdsRequest parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
return new SearchGoogleAdsRequest(input, extensionRegistry);
}
};
private SearchGoogleAdsRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
this.memoizedIsInitialized = -1;
}
private SearchGoogleAdsRequest() {
this.memoizedIsInitialized = -1;
this.customerId_ = "";
this.query_ = "";
this.pageToken_ = "";
this.summaryRowSetting_ = 0;
}
I am able to bypass this , by configuring MockMaker. But MockMaker doesn't work with PowerMockito and gives errors (cannot initialize plugin and that certain loader was supposed to go with Mockito but is loading with PowerMockito).
I need to use Power Mockito because I need to mock local scope objects and other unit tests created by others break with MockMaker.
I resolved this using newBuilder to create an object of the class and bypass the issue. It doesn't really mock the class, but I could use this in my case as an argument to a class which I needed to mock. If we don't require PowerMockito then we can use Mockito along with MockMaker, where these classes can be easily mocked though.
final SearchGoogleAdsRequest searchGoogleAdsRequest = SearchGoogleAdsRequest.newBuilder().build();
final GoogleAdsServiceClient mockGoogleAdsServiceClient = mock(GoogleAdsServiceClient.class);
final GoogleAdsServiceClient.SearchPagedResponse searchPagedResponse =
mock(GoogleAdsServiceClient.SearchPagedResponse.class);
when(mockGoogleAdsServiceClient.searchPagedCallable()).thenReturn(callable);
when(mockGoogleAdsServiceClient.searchPagedCallable().call(searchGoogleAdsRequest)).thenReturn(searchPagedResponse);
when(searchPagedResponse.iterateAll()).thenReturn(Arrays.asList(mockGoogleAdsRow));
when(mockGoogleAdsServiceClient.search(any())).thenReturn(searchPagedResponse);

How can I test java NIO static methods?

How can I test java NIO static methods?
I have a method
public Stream<String> getFileInformation(Provider provider) {
Path feedDirectoryPath;
DirectoryStream<Path> stream;
List<Path> files = new ArrayList<>();
try {
feedDirectoryPath = getFeedDirectoryPath();
if (!isFileExists(feedDirectoryPath)) {
return null;
}
stream = getDirectoryStream(feedDirectoryPath);
for(Path p : stream) {
files.add(p);
}
return Files.lines(Paths.get(files.stream().toString()));
} catch (Exception e) {
return null;
}
}
I have to test this method, but once my test arrives at Files.lines(..), I am stuck. I don't know how to assert this, and of-course if I were to pass a filePath like Path filePath = Paths.get("dummy.txt");
A NoSuchFileException would be thrown. Any help in testing this method would be greatly appreciated.
You could either supply it with an actual file (for instance in the src\test\resources folder if you're using maven), or mock the static method using PowerMock. I would recommend the first, it should be possible to load a file (for instance relative to your test class using classloader) and use this as input to the method under test.

Mockito; verify method was called with list, ignore order of elements in list

I have a class (ClassA) that get the files in a directory. It scans the given directory for files matching a regex. For each matching file, it adds a File Object to a list.
Once the directory is processed, it passes the List of Files to another Class (ClassB) for processing
I am writing unit tests for ClassA, so am mocking ClassB using Mockito, and injecting it into ClassA.
I then want to verify in different scenarios the contents of the list that is passed to ClassB (ie my mock)
I've stripped back the code to the following
public class ClassA implements Runnable {
private final ClassB classB;
public ClassA(final ClassB classB) {
this.classB = classB;
}
public List<File> getFilesFromDirectories() {
final List<File> newFileList = new ArrayList<File>();
// ...
return newFileList;
}
public void run() {
final List<File> fileList = getFilesFromDirectories();
if (fileList.isEmpty()) {
//Log Message
} else {
classB.sendEvent(fileList);
}
}
}
The test class looks like this
#RunWith(MockitoJUnitRunner.class)
public class AppTest {
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Mock
private ClassB mockClassB;
private File testFileOne;
private File testFileTwo;
private File testFileThree;
#Before
public void setup() throws IOException {
testFileOne = folder.newFile("testFileA.txt");
testFileTwo = folder.newFile("testFileB.txt");
testFileThree = folder.newFile("testFileC.txt");
}
#Test
public void run_secondFileCollectorRun_shouldNotProcessSameFilesAgainBecauseofDotLastFile() throws Exception {
final ClassA objUndertest = new ClassA(mockClassB);
final List<File> expectedFileList = createSortedExpectedFileList(testFileOne, testFileTwo, testFileThree);
objUndertest.run();
verify(mockClassB).sendEvent(expectedFileList);
}
private List<File> createSortedExpectedFileList(final File... files) {
final List<File> expectedFileList = new ArrayList<File>();
for (final File file : files) {
expectedFileList.add(file);
}
Collections.sort(expectedFileList);
return expectedFileList;
}
}
The problem is that this test works perfectly fine on windows, but fails on Linux. The reason being that on windows, the order that ClassA list the files matches the expectedList, so the line
verify(mockClassB).sendEvent(expectedFileList);
is causing the problem expecetdFileList = {FileA, FileB, FileC} on Windows, whereas on Linux it will be {FileC, FileB, FileA}, so the verify fails.
The question is, how do I get around this in Mockito. Is there any way of saying, I expect this method to be be called with this parameter, but I don't care about the order of the contents of the list.
I do have a solution, I just don't like it, I would rather have a cleaner, easier to read solution.
I can use an ArgumentCaptor to get the actual value passed into the mock, then can sort it, and compare it to my expected values.
final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(mockClassB).method(argument.capture());
Collections.sort(expected);
final List<String> value = argument.getValue();
Collections.sort(value);
assertEquals(expecetdFileList, value);
As noted in another answer, if you don't care about the order, you might do best to change the interface so it doesn't care about the order.
If order matters in the code but not in a specific test, you can use the ArgumentCaptor as you did. It clutters the code a bit.
If this is something you might do in multiple tests, you might do better to use appropriate Mockito Matchers or Hamcrest Matchers, or roll your own (if you don't find one that fills the need). A hamcrest matcher might be best as it can be used in other contexts besides mockito.
For this example you could create a hamcrest matcher as follows:
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyMatchers {
public static <T> Matcher<List<T>> sameAsSet(final List<T> expectedList) {
return new BaseMatcher<List<T>>(){
#Override
public boolean matches(Object o) {
List<T> actualList = Collections.EMPTY_LIST;
try {
actualList = (List<T>) o;
}
catch (ClassCastException e) {
return false;
}
Set<T> expectedSet = new HashSet<T>(expectedList);
Set<T> actualSet = new HashSet<T>(actualList);
return actualSet.equals(expectedSet);
}
#Override
public void describeTo(Description description) {
description.appendText("should contain all and only elements of ").appendValue(expectedList);
}
};
}
}
And then the verify code becomes:
verify(mockClassB).sendEvent(argThat(MyMatchers.sameAsSet(expectedFileList)));
If you instead created a mockito matcher, you wouldn't need the argThat, which basically wraps a hamcrest matcher in a mockito matcher.
This moves the logic of sorting or converting to set out of your test and makes it reusable.
An ArgumentCaptor probably is the best way to do what you want.
However, it seems that you don’t actually care about the order of the files in the List. Therefore, have you considered changing ClassB so that it takes an unordered collection (like a Set) instead?
A one-liner using argThat which compares the two lists as sets:
verify(mock).method(argThat(list -> new HashSet<>(expected).equals(new HashSet<>(list))));
You can use an ArgumentCaptor and then Hamcrest's Matchers.containsInAnyOrder() for assertion like this:
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(mockClassB).method(argument.capture());
List<String> value = argument.getValue();
assertThat(value, containsInAnyOrder("expected", "values");

Mock a private static field with JMockit?

I have a class like the following;
class ClassA {
private static File myDir;
// myDir is created at some stage
private static String findFile(final String fileName) {
for (final String actualBackupFileName : myDir.list()) {
if (actualBackupFileName.startsWith(removeExtensionFrom(backupFile))) {
return actualBackupFileName;
}
}
}
}
So, basically, I want to test this class by mocking out the File class so that when list() is called on it it returns a list of strings I define in my test class.
I've got the following but its not working at the minute, there's probably something obvious I'm doing wrong - I'm new to JMockit - any help is much appreciated!
#Mocked("list") File myDir;
#Test
public void testClassA() {
final String[] files = {"file1-bla.txt"};
new NonStrictExpectations() {{
new File(anyString).list();
returns(files);
}};
String returnedFileName = Deencapsulation.invoke(ClassA.class, "findFile","file1.txt");
// assert returnedFileName is equal to "file1-bla.txt"
}
When running the above test I get a NullPointerException for the myDir field in ClassA - so it looks like its not getting mocked properly?
You can use the setField method from the Deencapsulation class. Note example below:
Deencapsulation.setField(ClassA, "File", your_desired_value);
JMockit (or any other mocking tool) does not mock fields or variables, it mocks types (classes, interfaces, etc.) Where the instances of those types get stored inside the code under test is not relevant.
Example test for ClassA:
#Test
public void testClassA(#Mocked File myDir)
{
new Expectations() {{ myDir.list(); result = "file1-bla.txt"; }};
String returnedFileName = new ClassA().publicMethodThatCallsFindFile("file1.txt");
assertEquals("file1-bla.txt", returnedFileName);
}
The above should work. Note that testing private methods directly (or accessing private fields) is considered bad practice, so I avoided it here. Also, it's best to avoid mocking the File class. Instead, test only your public methods, and use actual files instead of mocking the filesystem.
try out this:
new Expectations {{
invoke(File.class, "list", null, null);
returns(files);
}}

Using JMockit to return actual instance from mocked constructor

I've looked at the following question and it is not the same as mine:
jMockit: How to expect constructor calls to Mocked objects?
This question is similar but the answer is not helpful to me:
How to mock the default constructor of the Date class with JMockit?
What I am trying to do is mock a constructor call to java.util.zip.ZipFile, specifically the one that has a java.io.File argument. I would like for the constructor to return an instance of a different ZipFile, one I will instantiate with the constructor that only takes a String argument.
This constructor call takes place inside a method under test, so I can't inject the ZipFile I want as a parameter.
For example, the code looks something like this:
public void whatever() {
//some code
//some more code
foo();
//yet more unrelated code
}
private Blah foo() {
ZipFile zf;
//a bunch of code we don't care about
zf = new ZipFile(someFile);// I want to give it a known zipfile! mock this!
// some more code we don't care about
Enumeration<?> entries = zf.entries();
ZipEntry entry = (ZipEntry) entries.nextElement();
InputStream is = zf.getInputStream(entry)
//maybe some other calls to the ZipFile
// do something else
}
My first thought was to do the following with static partial mocking:
final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
#Mocked("(java.io.File)")
ZipFile zf;
{
new ZipFile((File) any); result = test;
}
};
But this won't work as indicated by this line in the tutorial: constructors have void return type, so it makes no sense to record return values for them
My second thought was to try the following:
new NonStrictExpectations() {
{
newInstance("java.util.zip.ZipFile", new File("path/to/actual.zip"));
}
};
But this throws the following when trying to initialize the file:
java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(Unknown Source)
at java.util.zip.ZipFile.<init>(Unknown Source)
My third thought was to use a #MockClass as below:
#Before
public void setUp() throws Exception {
Mockit.setUpMocks(MockedZipFile.class);
}
#After
public void tearDown() {
Mockit.tearDownMocks();
}
#MockClass(realClass=ZipFile.class)
public static class MockedZipFile {
public ZipFile it;
#Mock
public void $init(File f) throws ZipException, IOException {
it = new ZipFile("path/to/actual.zip");//this is what would be called
}
}
But this hoses some other mocks I have that load a configuration file for a different part of my test class. Not to mention I will want different zip files for different test cases.
I suppose I could mocking everything the ZipFile would do, but this would quickly become a giant pain as it's called lots of places, it's output would be need to be mocked, etc, etc. Refactoring to try to make this accessible would be awkward, as the code that uses the ZipFile is internal to the code and the public methods don't really care about it.
I have a feeling there is a way for JMockit to allow this (giving a particular instance of an object when a constructor is called), but I can't figure it out. Does anyone have any ideas?
EDIT: I tried the method suggested by #Rogerio, but I have a new error. Here's my setup:
final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
ZipFile zf;
{
zf.entries();
result = test.entries();
zf.getInputStream((ZipEntry) any);
result = new Delegate() {
InputStream getInputStream(ZipEntry entry) throws IOException {
return test.getInputStream(entry);
}
};
}
};
but I get the following stack trace:
java.lang.InternalError
at path.to.test.ExtractDataTest$1.<init>(ExtractDataTest.java:61)
at path.to.test.ExtractDataTest.setUp(ExtractDataTest.java:61)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
where line 61 is the new NonStrictExpectations() { line.
I really want to say "instead of mocking this object, substitute this other object of the same type". Maybe I have expressed that poorly.
EDIT2: I figured I should include version numbers:
Using Eclipse 3.6.1
Java 1.6.0_26
JMockit 0.999.10
JMockit can mock the ZipFile class, but it interferes with class loading since the JarFile subclass is used by the JVM all the time (whenever it loads a class from a jar file in the classpath). Currently, there is no easy way to avoid this interference (there is a plan to "fix" this, but it will take time).
However, this particular test case isn't very suited for a mocking tool anyway. Instead, I would recommend setting up the test so that it provides an actual zip file with the desired contents in the proper place.
(another edit)
I just applied a change to JMockit (for release 0.999.12) which allows the following test to pass, provided there is a test.zip file in the working dir, and it contains a text file whose first line is "test":
#Test
public void mockZipFile() throws Exception
{
final ZipFile testZip = new ZipFile("test.zip");
new NonStrictExpectations() {
#Capturing #Injectable ZipFile mock;
{
mock.entries(); result = testZip.entries();
mock.getInputStream((ZipEntry) any);
result = new Delegate() {
InputStream delegate(ZipEntry e) throws IOException {
return testZip.getInputStream(e);
}
};
}
};
ZipFile zf = new ZipFile("non-existing");
ZipEntry firstEntry = zf.entries().nextElement();
InputStream content = zf.getInputStream(firstEntry);
String textContent = new BufferedReader(new InputStreamReader(content)).readLine();
assertEquals("test", textContent);
}
However, I would still recommend not using a mocking API for cases like this. Instead, use a real file.
This probably won't help you, but if you were using Mockito or EasyMock, you could add PowerMock, which allows you to mock the construction of new objects in your code under test.

Categories