I am testing some legacy code and would like to mock any calls to a static logger method: LoggerFact.getLogger(Class class, String MethodName), here is what I tried:
#RunWith(PowerMockRunner.class)
#PrepareForTest({LoggerFact.class, MyClass.class, Logger.class})
public class MyClassTest
{
#Before
public void prepare() throws Exception
{
Logger mockedLogger = Mockito.mock(Logger.class);
PowerMockito.mockStatic(LoggerFact.class);
PowerMockito.when(LoggerFact.getLogger(MyClass.class, "test"))
.thenReturn(mockedLogger);
}
//My tests
}
The class that I am testing:
public class MyClass
{
public String methodToBeTested()
{
Logger logger = LoggerFact.getLogger(this.getClass(), "test");
logger.info("This is a test");
//some logic
return "SUCCESS";
}
}
But I am recieving this error when I do this from the prepare when():
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
Did I miss something? I checked a lot of older posts about this issue, but nothing worked for me.
Working example here. Tested with JDK 8 and JDK 11.
The following works for me. (I have found that the order of initialization can be crucial):
#RunWith(PowerMockRunner.class)
#PrepareForTest(LoggerFact.class)
public class MyClassTestCase {
private MyClass myClass = null;
#Before
public void setUp() {
PowerMockito.mockStatic(LoggerFact.class);
}
#Test
public void testMethodToBeTested() {
Logger mockLogger = Mockito.mock(Logger.class);
PowerMockito.when(LoggerFact.getLogger(eq(MyClass.class),eq("test"))).thenReturn(mockLogger);
myClass = new MyClass();
// test
myClass.methodToBeTested();
verify(mockLogger, times(1)).info(eq("This is a test"));
}
}
As requested, from build.gradle in the example link above, here are the versions of libraries:
dependencies {
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.mockito:mockito-core:3.6.0'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.7'
testImplementation 'org.powermock:powermock-module-junit4:2.0.7'
}
Related
In a unit test in a spring-boot environment with slf4j and logback, I want to make sure LOGGER.isTraceEnabled() returns true for a particular test class only.
The reason for that is, that sometimes we have slow and non trivial code guarded with if (LOGGER.isTraceEnabled()) {...}. In a unit test we want to make sure it does not break the application if we switch on trace.
Code under test:
public class ClassUnderTest{
private static final Logger LOGGER = LoggerFactory.getLogger(ClassUnderTest.class);
public void doSomething(Calendar gegenwart) {
if (LOGGER.isTraceEnabled()) {
// non trivial code
}
}
}
Test code:
public class ClassUnderTestUnitTest{
#Test
public void findeSnapshotMitLueke() {
ClassUnderTest toTestInstance = new ClassUnderTest ();
// I want to make sure trace logging is enabled, when
// this method is executed.
// Trace logging should not be enabled outside this
// test class.
toTestInstance.doSomething(Calendar.getInstance());
}
}
You can easily create a static mock of LoggerFactory using PowerMock and cause it to return a regular mock of Logger (using EasyMock). Then you simply define mock implementation of Logger.isTraceEnabled() and Logger.trace().
Off the top of my head:
#PrepareForTest({ LoggerFactory.class })
#RunWith(PowerMockRunner.class) // assuming JUnit...
public class ClassUnderTestUnitTest{
#Test
public void findeSnapshotMitLueke() {
Logger mockLogger = EasyMock.createMock(Logger.class);
EasyMock.expect(mockLogger.isTraceEnabled()).andReturn(true);
EasyMock.expect(mockLogger.trace(any()));
EasyMock.expectLastCall().anyTimes() // as trace is a void method.
// Repeat for other log methods ...
PowerMock.mockStatic(LoggerFactory.class);
EasyMock.expect(LoggerFactory.getLogger(ClassUnderTest.class)
.andReturn(mockLogger);
PowerMock.replay(mockLogger, LoggerFactory.class);
ClassUnderTest toTestInstance = new ClassUnderTest ();
// I want to make sure trace logging is enabled, when
// this method is executed.
// Trace logging should not be enabled outside this
// test class.
toTestInstance.doSomething(Calendar.getInstance());
// After the operation if needed you can verify that the mocked methods were called.
PowerMock.verify(mockLogger).times(...);
PowerMock.verifyStatic(LoggerFactory.class).times(...);
}
}
In case you don't want to use a framework like powermock you can do the following trick:
public class ClassUnderTest {
private Supplier<Logger> loggerSupplier = () -> getLogger(ClassUnderTest.class);
public void doSomething(Calendar gegenwart) {
if (loggerSupplier.get().isTraceEnabled()) {
// non trivial code
}
}
}
Now you are able to mock the logger:
public class ClassUnderTestUnitTest{
#Mock
private Supplier<Mock> loggerSupplier;
#Mock
private Logger logger;
#Test
public void findeSnapshotMitLueke() {
ClassUnderTest toTestInstance = new ClassUnderTest ();
when(loggerSupplier.get()).thenReturn(logger);
when(logger.isTraceEnabled()).thenReturn(true)
toTestInstance.doSomething(Calendar.getInstance());
// verifyLogger();
}
}
As per my knowledge, We can Mock the private method in same class by using PowerMockito.
With in the same class is working fine for me , but when i'm calling private method from the other class it's not working.
Below Example i've 2 classes , Service class and Helper classes
Helper class having private method.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Helper.class,Service.class })
#PowerMockIgnore("javax.management.*")
public class EPartnerBatchServiceTest {
private Helper helper;
#InjectMocks
private ServiceClass serviceClass;
#Before
public void setUp() throws Exception {
helper = PowerMockito.spy(new Helper());
ServiceClass = PowerMockito.spy(new serviceClass());
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpdateIndividualUserStatus() throws Exception {
PowerMockito.doReturn("Test").when(helper, "privateMethod", anyString(), Matchers.anyObject());
String response = serviceClass.update(loggerId, activityLogDTO);
}
}
Sample Classes :
Class A{
value=new B().method1();
}
Class B{
public method1(){
value = method2();
}
private method2(){
return "Test";
}
}
You shouldn't be worrying with testing explicitly your private methods, since they are not accessible for the ones calling it, it's function should be tested somewhere in the flow of your public methods. But, if for some reason you need to test them explicitly, then maybe reflections and setting those methods as accessible for testing may resolve your problem.
You'll find great examples here: https://www.baeldung.com/java-method-reflection
I am trying to mock an Impl that contains 2 static members A, B, and a static method Utils.SomeMethod. I tried to mix PowerMock and Mockito initially but was not sure if this was causing the problem so I changed all refs to PowerMockito. I get unit test failures that mocks are not getting invoked. if I remove statics and just use Mockito then all tests succeed.
Here is a brief outline of the problem.
class Impl {
static A a;
static B b;
private static final String s = Utils.SomeMethod();
void mainMethod() {
a.aMethod("foo");
b.bMethod("bar");
}
}
So in my unit test I have
#PowerMockIgnore({"javax.net.ssl.*" , "javax.crypto.*"})
#RunWith(PowerMockRunner.class)
#PrepareForTest({Utils.class})
public class ImplTest {
A a;
B b;
#Captor
ArgumentCaptor<String> argumentCaptor;
#BeforeClass
static public void setUp() {
PowerMockito.mockStatic(Utils.class);
PowerMockito.when(Utils.SomeMethod()).thenReturn("test"); // works
}
#Before
public void before() {
a = PowerMockito.mock(A.class);
b = PowerMockito.mock(B.class);
impl = PowerMockito.mock(Impl.class);
impl.setA(a); // I tried #Mock and #InjectMocks but seemed to not work on statics, works with non static members
impl.setB(b);
}
#Test
public void test() {
PowerMockito.when(a
.aMethod(any(String.class))
.thenReturn("hmm");
PowerMockito.when(b.bMethod(any(String.class))
.thenReturn("yo");
impl.mainMethod();
verify(a, times(1)).aMethod(argumentCaptor.capture());
// fails that 0 times mock was invoked
}
}
As I can see it you are mocking Impl but you should instantiate it if you want mainMethod invoke your static methods.
Also, are you initializing argumentCaptor somewhere in your code?
I'd like to suggest using Mockito for most testing mocks, and using PowerMockito only when dealing with some static methods. With slight changes, your test code worked ok:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Utils.class })
public class ImplTest {
#Mock
A a;
#Mock
B b;
#Captor
ArgumentCaptor<String> argumentCaptor;
#BeforeClass
static public void setUp() {
PowerMockito.mockStatic(Utils.class);
Mockito.when(Utils.SomeMethod()).thenReturn("test"); // works
}
Impl impl;
#Before
public void before() {
Impl.setA(a);
Impl.setB(b);
impl = new Impl();
}
#Test
public void test() {
Mockito
.when(a.aMethod(Matchers.any(String.class)))
.thenReturn("hmmm");
Mockito
.when(b.bMethod(Matchers.any(String.class)))
.thenReturn("yo");
impl.mainMethod();
Mockito.verify(a, Mockito.times(1)).aMethod(argumentCaptor.capture());
Assert.assertEquals("foo", argumentCaptor.getValue());
Mockito.verify(b, Mockito.times(1)).bMethod(argumentCaptor.capture());
Assert.assertEquals("bar", argumentCaptor.getValue());
}
}
Please notice that if A and B are defined as static, they should be injected into the class, not into individual instance(s).
#InjectMocks will not work in this context, since it requires a different Runner. Please have a look at this other article Difference between #Mock and #InjectMocks
I'm trying to use JMockit to test that a certain logging operation takes place:
public class LogClass1 {
public void doLog() {
Logger logger = LogManager.getLogger(LogClass1.class);
logger.info("This is a log message for {}", "arg1");
}
}
public class LogClass1Test {
#Mocked
private Logger logger;
#Tested
private LogClass1 x;
#Before
public void setup() {
x = new LogClass1();
}
#Test
public void testDoLog() {
new Expectations() {
{
logger.info("This is a log message for {}", "arg1");
}
};
x.doLog();
}
}
But this results in a "missing 1 invocation to org.apache.logging.log4j.Logger#info" error.
I've done similar mocking with log4j 1.x in the past, and I haven't had this problem. I'm wondering if there's some issue because log4j 2.x seems to have many more overloads of its info() methods.
I tried changing "arg1" to (Object)"arg1" in the unit test to see if I could get it to match the signature. This didn't help.
Any thoughts on how I can get this to work?
Note that Logger is an interface, and that LogClass1 obtains an instance of it through the LogManager.getLogger static factory method. So, obviously, it creates an instance of some Logger implementation class. And said class is not being mocked in the test.
What the test needs to do is to mock LogManager, so it returns the #Mocked Logger instance. That is, add a #Mocked LogManager field to the test class.
(Also, no need for that setup method since #Tested creates an instance automatically.)
Trying to use mockito in my AndroidTestCase. I added the dependencies to the build.gradle:
final DEXMAKER_VERSION = '1.2'
dependencies {
// ...
androidTestCompile "com.google.dexmaker:dexmaker:${DEXMAKER_VERSION}"
androidTestCompile "com.google.dexmaker:dexmaker-mockito:${DEXMAKER_VERSION}"
androidTestCompile 'org.mockito:mockito-core:1.10.19'
}
The TestCase with the mockito initialization:
public class UsersListPresenterTest extends AndroidTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
public void testInitialize() throws Exception {
}
}
But as soon as I add any attribute to the class, even before adding any annotation the test start to crash:
public class UsersListPresenterTest extends AndroidTestCase {
String mockString;
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
public void testInitialize() throws Exception {
}
}
With the following stacktrace
java.lang.NullPointerException: Attempt to invoke virtual method
'java.lang.Class java.lang.Object.getClass()' on a null object reference
at com.google.dexmaker.mockito.DexmakerMockMaker.getInvocationHandlerAdapter(DexmakerMockMaker.java:80)
at com.google.dexmaker.mockito.DexmakerMockMaker.getHandler(DexmakerMockMaker.java:75)
at org.mockito.internal.util.MockUtil.isMockitoMock(MockUtil.java:74)
at org.mockito.internal.util.MockUtil.isMock(MockUtil.java:66)
at org.mockito.internal.configuration.injection.scanner.MockScanner.isMockOrSpy(MockScanner.java:86)
at org.mockito.internal.configuration.injection.scanner.MockScanner.preparedMock(MockScanner.java:72)
at org.mockito.internal.configuration.injection.scanner.MockScanner.scan(MockScanner.java:61)
at org.mockito.internal.configuration.injection.scanner.MockScanner.addPreparedMocks(MockScanner.java:47)
at org.mockito.internal.configuration.InjectingAnnotationEngine.injectMocks(InjectingAnnotationEngine.java:96)
at org.mockito.internal.configuration.InjectingAnnotationEngine.processInjectMocks(InjectingAnnotationEngine.java:62)
at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:56)
at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:108)
at com.myproject.presentation.UsersListPresenterTest.setUp(UsersListPresenterTest.java:28)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)
What am I doing wrong?
You could try to replace
MockitoAnnotations.initMocks(this);
with this
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath());
It works for me. See ref here
Its a bug in dexmaker for which I have submitted a fix:
https://github.com/crittercism/dexmaker/pull/24
For me the solution was to use the Method Mockito.mock() for each Mocking Object instead of using MockitoAnnotations.initMocks(this);
So for example:
public class HomePresenterTest {
private Repository repository;
private HomePresenter presenter;
#Before
public void before() {
repository = mock(Respository.class);
presenter = new HomePresenter(repository);
}
//Your tests
}
I've created an issue there https://github.com/mockito/mockito/issues/392
Original answer with hotfix there https://stackoverflow.com/a/36642606/1224247