Prevent special class from static mocking - java

I have a class named SpecialClass. This class has the method doSomething in it. This class is in this particular manner special, that I do not want to mock this class at all via MockedStatic. It is very easy for me to remeber that I don't want to mock this class, but there are other people programming at the same project and they should know to not mock this class but I can not relate on it.
Given this special test:
#Test
void testSomething(){
try(MockedStatic<SpecialClass> mock = Mockito.mockStatic(SpecialClass.class)){
mock.when(SpecialClass::doSomething).thenReturn(0);
System.out.println(SpecialClass.doSomething());
}
}
I dont want to let this test execute at all. But it will execute, because Mockito does not know about my SpecialClass and not to mock it. There could be also various other locations where SpecialClass could be mocked.
Some classes can not be mocked because of Mockito decides so. Given this mock of java.lang.System:
#Test
void testFail(){
try(MockedStatic<System> sys = Mockito.mockStatic(System.class)){
// some code
}
}
I get the exception org.mockito.exceptions.base.MockitoException: It is not possible to mock static methods of java.lang.System to avoid interfering with class loading what leads to infinite loops, which is ok.
My question is: Can I somehow have some global project settings (or some annoations) to mark my class SpecialClass to be not used in Mockitos MockedStatic? I would be also ok if I can also not mock this class in a normal way.
I tried to find some information regarding restricting mocks, but i was unable to find some.
My mockito version of mockito-core and mockito-inline is 4.3.1

I used a junit test to read all my files and trying to detect all mocks.
Please note that my test is working in a very special assumption that all of my test classes are in the same directory. This will hoever not be given in every project. In this case you will need to find the files recursivly.
class NoStaticMockTest {
#Test
void testNoMocks() throws IOException {
// Base dir of my tests
File testDir = new File("src/test/java");
// reading all Files in the test directory excluding this file
for (File file : testDir.listFiles(pathname -> !pathname.getAbsolutePath().endsWith("NoStaticMockTest.java") && pathname.isFile())) {
try (FileInputStream fis = new FileInputStream(file)) {
// Reading the contennt and trying to find MockedStatic<SpecialClass>
String content = IOUtils.toString(fis);
if (content.contains("MockedStatic<SpecialClass>")) {
Assertions.fail(file.getAbsolutePath() + " used MockedStatic<SpecialClass> which is not allowed");
}
}
}
}
}

Related

Can i mock file.mkdirs() which is inside of a method

Here is my legacy code below. I don't want to create an actual folder. Instead I want to create a temp folder, which has to be deleted after test execution is over.
Public class MainClass() {
public void m1() {
m2();
}
private void m2() {
File f=new File();
f.mkrdirs()
}
}
Here is my TestClass
public class TestClass{
#Rule
public TemporaryFolder folder= new TemporaryFolder();
public void testM1(){
File fileSpy = mock(File.class);
Mockito.doNothing().when(fileSpy.mkdir());
}
}
but not working.Any clues?
Your problem starts here:
File f=new File();
Your production code calls new for the file class.
In your test code, you do:
File fileSpy = mock(File.class);
And how is that spy related to your f instance? Answer: not at all.
Declaring a spy in your test doesn't magically make your production code use it! So you have to make sure that when your production code uses that f object ... f should be your fileSpy
Now you have these options:
see if you can change your production code, so that you can somehow pass in a file instance. Either by making it an argument, that you really can pass something to, or by turning f into a field of your class, to then use Mockito's #InjectMock annotation
if you are stuck with your production code, Mockito won't do. Then you need PowerMock(ito) or JMockit.
Typically, I advise people to stick with plain Mockito, and avoid mocking new() (even more so mocking static calls) via PowerMock(ito) like the plague.
But testing legacy code is the one situation where you have to choose between "not testing at all" or "testing with PowerMock(ito)". So, if you can't change your production code, then your only choice would be to mock that call to new(). See here for how to do that.

How to make the unit test execute a particular test case everytime when it sees a certain function in the executing java project?

I am having a build failure issue while running a bunch of unit test over a java project. I am getting the NoClassDefFoundError which is happening because of the lack of ability for the unit test to get the dependencies. I am trying to mock an object for the class and then call the function, but the code is structured in a way that is getting a bit complex for me to handle the issue. I am very new to unit testing. I have provided below, a sample of code structure that my project has
Class ServiceProvider(){
obj declarations;
public void mainFunction(){
//Does a couple of things and calls a function in another class
boolean val = subFunction();
}
public boolean subFunction(){
boolean val = AnotherClass.someFunction(text);
//this function throws lots of exceptions and all those are caught and handled
return val;
}
#RunsWith(MockitoJUnitRunner.class)
Class UnitTestBunch(){
#Mock
AnotherClass acObj = new AnotherClass();
#InjectMock
ServiceProvider sp = new ServiceProvider();
#Test
public void unitTest1() throws Exception{
when(acObj.someFunction(text)).thenReturn(true);
}
#Test
public void unitTest2() throws Exception{
thrown.expect(ExceptionName.Class);
sp.mainFunction();
}
I have a test that uses the mock object and performs the function call associated with that class. But, the issue here is that there are a bunch of other unit test cases that are written similar to the unitTest2 function and calls the mainFunction at the end of the test. This mainFunction invokes someFunction() and causes NoCalssDefFoundError(). I am trying to make the unit test execute the content in unitTest1 everytime when it sees the AnotherClass.someFunction(). I am not sure if this is achievable or not. There could be another better way to resolve this issue. Could someone please pitch in some ideas?
In your test you seem to be using unitTest1 for setup, not for testing anything. When you run a unit test, each test should be able to run separately or together, in any order.
You're using JUnit4 in your tests, so it would be very easy to add the statement you have in unitTest1 into a #Before method. JUnit4 will call this method before each test method (annotated with #Test).
#Before
public void stubAcObj() throws Exception{
when(acObj.someFunction(text)).thenReturn(true);
}
The method may be named anything, though setUp() is a common name borrowed from a method to override in JUnit3. However, it must be annotated with org.junit.Before.
If you need this from multiple test cases, you should just create a helper, as you would with any code. This doesn't work as well with #InjectMocks, but you may want to avoid using #InjectMocks in general as it will fail silently if you add a dependency to your system-under-test.
public class AnotherClassTestHelper {
/** Returns a Mockito mock of AnotherClass with a stub for someFunction. */
public static AnotherClass createAnotherClassMock() {
AnotherClass mockAnotherClass = Mockito.mock(AnotherClass.class);
when(mockAnotherClass.someFunction(text)).thenReturn(true);
return mockAnotherClass;
}
}
As a side note, this is a counterintuitive pattern:
/* BAD */
#Mock
AnotherClass acObj = new AnotherClass();
You create a new, real AnotherClass, then instruct Mockito to overwrite it with a mock (in MockitoJUnitRunner). It's much better just to say:
/* GOOD */
#Mock AnotherClass acObj;

jUnit4.0 : how to know current test method name?

I've implemented a feature in my jUnit tests that takes, for every test case, a fresh copy of a data source. This copy is taken in a folder specific for each test case. The idea is that every test case can start from a clean situation, manipulate it and let it as such after the run. This is often useful when the test fails for analysing the problem.
For now I have to call this feature directly in the test method because I don't know how to retrieve the current test name:
public void testTest1() {
TestHelper th=TestHelper.create("testTest1",subPathToDataSource);
// do the test...
Path dataPath = th.getDataPath();
...
}
I would like to be able to write something like this:
Path dataPath;
#Before
public initTest() {
th=TestHelper.create(SomeJUnitObject.getCurrentTestName(),subPathToDataSource);
...
}
public void testTest1() {
// do the test...
Path dataPath = th.getDataPath();
...
}
Until now I found as answers : "You don't need to know that"... But I do need it !
Is this possible ?
Kind regards
Look at the TestName rule.
You should be able to add in your test class:
#Rule TestName name=new TestName();
And then access it.
(On phone, so can't check versions support/details - might be 4.x only)
Here is an alternative approach; create an abstract class which your "real" test classes inherit.
I have several such examples in my projects and here I will give one, mainly testing for individual JSON Patch operations.
All my test files are JSON, and located under an appropriately named resource directory. The base, abstract class is JsonPatchOperationTest. And here is the full code of AddOperationTest which tests for JSON Patch's add operation:
public final class AddOperationTest
extends JsonPatchOperationTest
{
public AddOperationTest()
throws IOException
{
super("add");
}
}
And that's it! Not even one test method in this class, but of course your implementation may vary.
In your case you probably want to pass the directory name as a constructor argument, or the like.

Mock System class to get system properties

I have a folder path set in system variable through JVM arguments in Eclipse and I am trying to access it in my class as:
System.getProperty("my_files_path").
While writing junit test method for this class, I tried mocking this call as test classes do not consider JVM arguments. I have used PowerMockito to mock static System class and tried returning some path when System.getProperpty is being called.
Had #RunWith(PowerMockRunner.class) and #PrepareForTest(System.class) annotations at class level. However, System class is not getting mocked as a result I always get null result.
Any help is appreciated.
Thanks Satish. This works except with a small modification. I wrote PrepareForTest(PathFinder.class), preparing the class I am testing for test cases instead of System.class
Also, as mock works only once, I called my method right after mocking.
My code just for reference:
#RunWith(PowerMockRunner.class)
#PrepareForTest(PathInformation.class)
public class PathInformationTest {
private PathFinder pathFinder = new PathFinder();
#Test
public void testValidHTMLFilePath() {
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty("my_files_path")).thenReturn("abc");
assertEquals("abc",pathFinder.getHtmlFolderPath());
}
}
There are certain classes PowerMock can't mock in the usual way. See here:
https://code.google.com/p/powermock/wiki/MockSystem
This, however, may still not work. In order of "good design" preference, you can fall back to these:
Refactor your code! Using a System property for passing a file path around is probably not the best way. Why not use a properties file loaded into a Properties object? Why not use getters/setters for the components that need to know this path? There are many better ways to do this.
The only reason I could think of not to do this is you're trying to wrap a test harness around code you "can't" modify.
Use #Before and #After methods to set the System property to some known value for the test(s). You could even make it part of the #Test method itself. This will be FAR easier than attempting to mock through PowerMock. Just call System.setProperty("my_files_path","fake_path");
System class is declared as final and cannot be mocked by libraries such as PowerMock. Several answers posted here are incorrect. If you are using Apache System Utils you can use getEnvironmentVariable method instead of calling System.getenv directly. SystemUtils can be mocked since it is not declared as final.
Set the system property in your test and ensure that it is restored after the test by using the rule RestoreSystemProperties of the library System Rules.
public class PathInformationTest {
private PathFinder pathFinder = new PathFinder();
#Rule
public TestRule restoreSystemProperties = new RestoreSystemProperties();
#Test
public void testValidHTMLFilePath() {
System.setProperty("my_files_path", "abc");
assertEquals("abc",pathFinder.getHtmlFolderPath());
}
}
The System.setter or getter method should be put in a user defined method and that method can be mocked to return the desired property in unit test.
public String getSysEnv(){
return System.getEnv("thisprp");
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(System.class)
public class MySuperClassTest {
#Test
public void test(){
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty("java.home")).thenReturn("abc");
System.out.println(System.getProperty("java.home"));
}
}
Sailaja add System.class because as per the power mock guidelines for static,private mocking you should add the class in prepare for test.
#PrepareForTest({PathInformation.class,System.class})
Hope this helps.let me know if it doesn't work

Simulate File in Java

I'm trying to write unit tests for a method that takes a String filename, then opens the file and reads from it. So, to test that method, I thought about writing a file, then calling my method. However, in the build farm, it is not possible to write files arbitrarily to disk. Is there a standard way to "simulate" having a real file in my unit test?
I've found that Mockito and Powermock are a good combination for this. Actually there's a blog post with a sample, where the File-class's constructor is mocked for testing purposes. Here's also a small example I threw together:
public class ClassToTest
{
public void openFile(String fileName)
{
File f = new File(fileName);
if(!f.exists())
{
throw new RuntimeException("File not found!");
}
}
}
Testing with Mockito + Powermock:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ClassToTest.class)
public class FileTest
{
#Test
public void testFile() throws Exception
{
//Set up a mocked File-object
File mockedFile = Mockito.mock(File.class);
Mockito.when(mockedFile.exists()).thenReturn(true);
//Trap constructor calls to return the mocked File-object
PowerMockito.whenNew(File.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(mockedFile);
//Do the test
ClassToTest classToTest = new ClassToTest();
classToTest.openFile("testfile.txt");
//Verify that the File was created and the exists-method of the mock was called
PowerMockito.verifyNew(File.class).withArguments("testfile.txt");
Mockito.verify(mockedFile).exists();
}
}
If you use JUnit, there is the TemporaryFolder. Files are deleted after the test. Example given on the page:
public static class HasTempFolder {
#Rule
public TemporaryFolder folder= new TemporaryFolder();
#Test
public void testUsingTempFolder() throws IOException {
File createdFile= folder.newFile("myfile.txt");
File createdFolder= folder.newFolder("subfolder");
// ...
}
}
However, I have also used it for testing my Android class read/write capabilities like:
[...]
pw = new PrintWriter(folder.getAbsolutePath() + '/' + filename);
pw.println(data);
How about using mocked stream classes, which override the real ones (like BufferredReader, File) completely (meaning all methods or all methods you use)? The data can be saved as an array of bytes, for example, in some singleton, if they are to be used between different test classes.
This is highly frowned upon:
The smallest amount of testable code. Often a single method/function,
sans the use of other methods or classes. Fast! Thousands of unit
tests can run in ten seconds or less! A unit test NEVER uses:
a database
an app server (or server of any kind)
file/network I/O or file system;
another application;
the console (System.out, System.err, etc.)
logging
most other classes (exceptions include DTO's, String, Integer, mocks and maybe a few others). "
Source
If you must read from a file, have a test file pre-generated that all unit tests read from. There is no need to write anything to disk.

Categories