How can I use PowerMockito to ensure Jenkins static method returns my mocked object?
I see that if I have a test, that Jenkins is the mock. However, if I add what looks like a valid PowerMockito.when for an additional static method, I get the error below. I'm stumped.
error
groovy.lang.MissingMethodException: No signature of method:
static jenkins.model.Jenkins.getItemByFullName() is applicable for argument types:
(java.lang.String) values: [job]
Possible solutions:
getItemByFullName(java.lang.String),
getItemByFullName(java.lang.String, java.lang.Class)
code
#RunWith(PowerMockRunner.class)
#PrepareForTest([Jenkins.class, Job.class])
class MyTest {
def thisScript
#Mock
private Jenkins jenkins
#Mock Job job
MyClass myClass
#Before
void setUp() {
PowerMockito.mockStatic(Jenkins.class)
PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins)
PowerMockito.when(Jenkins.getItemByFullName("job".toString())).thenReturn(job)
}
A major oops on my part. The getInstance method is static, getItemByFullName is not static. So, here's the fix
#RunWith(PowerMockRunner.class)
#PrepareForTest([Jenkins.class, Job.class])
class MyTest {
def thisScript
#Mock
private Jenkins jenkinsInstance
#Mock Job job
MyClass myClass
#Before
void setUp() {
PowerMockito.mockStatic(Jenkins.class)
PowerMockito.when(Jenkins.getInstance()).thenReturn(jenkins)
PowerMockito.when(jenkinsInstance.getItemByFullName("job".toString())).thenReturn(job)
}
I must mock the instance's method jenkinsInstance.getItemByFullName and not the Jenkins.class class's static method.
Related
Environment: Java11, AWS Lambda, Dagger, JUnit5 (5.9.1), Mockito(4.8.1) (tried both mockito core & inline)
I have classes A, B and C.
C is injected to B and B is injected to A.
Method A.ma1() calls B.mb1() and B.mb1 calls C.mc1()
I just want to verify if a method "mc1" of C is called or not if I call A.ma1
Here is my structure:
#ExtendWith(MockitoExtension.class)
class Test {
#InjectMocks
A a;
#Spy
B b;
#Mock
C c;
#Test
public void test() {
a.ma1();
verify(c).mc1(any());
}
}
The problem is when A.ma1() calls B.mb1(), B.mb1() behaves like a Mock instead of a Spy.
The contents of the method never being executed during debugging, but immediately returning. I also tried to annotate B with #InjectMocks along with #Spy. It also did not work, throwing a different kind of exception.
Does anybody have any idea why does B behave like a Mock instead of a Spy?
Added after comment:
Here is the complete code: (simplified)
AService is A, InternalService is B and PanelDao is C in the above example. B's "selectMappedInternal" never executes line by line but returns immediately like a mock when called.
#ExtendWith(MockitoExtension.class)
class Test {
#InjectMocks
AService aService ;
#Spy
InternalService internalService ;
#Mock
PanelDao panelDao;
#Test
public void test() {
..
}
}
public class AService {
#Inject
InternalService internalService;
#Inject
public AService() {
}
public void processMessage(AccSqsMessage accSqsMessage) {
..
internalSet = internalService.selectMappedInternal(accSqsMessage.getCode());
..
}
}
public class InternalServiceImpl implements InternalService {
#Inject
PanelDao panelDao;
#Inject
public InternalServiceImpl() {
}
#Override
public Set<Internal> selectMappedInternal(String code) {
..
Optional<Panel> p = panelDao.findPanelByCode(code);
..
}
}
Finally able to solve it as below.
My guess about cause of the problem is implementation class "InternalServiceImpl" for interface "InternalService" could not be injected properly to unit test class (despite it is working fine when injected to a business class), so during debugging, I was not able to see execution of methods in spy object.
After spying (without annonation) an explicitly provided implementation class, everything seems solved.
#ExtendWith(MockitoExtension.class)
class Test {
#InjectMocks
AService aService ;
#InjectMocks
InternalService internalService = Mockito.spy(InternalServiceImpl.class);
#Mock
PanelDao panelDao;
#Test
public void test() {
..
}
}
Now I have some other library beans that implement initializingBeans and add some time-consuming methods, but I don't want to actually execute them, I need the Spring environment, but this bean I can mock it to reduce the overall test time, what should I do
This is the pseudo code of the bean of Mock:
public final class TimeoutBean implements InitializingBean, ApplicationContextAware, ApplicationListener {
#Override
public void afterPropertiesSet() throws Exception {
initProc();
}
/**
* init
*/
public void initProc() {
//... Something time-consuming and irrelevant to this test
}
}
And my mock unit test code:
#SpringBootTest(classes = Application.class)
#DelegateTo(SpringJUnit4ClassRunner.class)
public class MockTest {
#MockBean
private TimeoutBean timeoutBean;
#Resource
private MyRepository myRepository;
#SneakyThrows
#Test
public void test() {
doNothing().when(timeoutBean).initProc();
myRepository.getById(1L);
}
}
In this way, the Mock initProc method is invalid. I can probably understand because #MockBean calls after InitializingBean#afterPropertiesSet. When the afterPropertiesSet is executed, TimeoutBean is not an Mock object, but I don't know how to solve it.
Your class TimeoutBean is final and mockito cannot mock final classes. So first you will have to fix that. After that I think it should work. For mocked classes you don't have to tell mockito to doNothing() becease that is the default behaviour for mocks.
I don't understand what you mean with: the Mock initProc method is invalid?
So I have a couple Tests that run using mockito. One requires a static to be mocked so I use PowerMockito like this...
BDDMockito.given(WebClient.create(any())).willReturn(webClientMock);
Next I have another test that mocks a final class like this...
HttpSecurity security = Mockito.mock(HttpSecurity.class);
The second one doesn't work at first, I get the following failure....
Cannot mock/spy class org.springframework.security.config.annotation.web.builders.HttpSecurity
Mockito cannot mock/spy because :
- final class
So I looked and found this question which suggests adding the file .../src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker with mock-maker-inline
But when I add that the first line fails with...
org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class java.lang.Class
How do I get both to work?
Update
I didn't want to clutter up the question with the entire test which really doesn't provide any extra context. However, since people are insisting and seem to misunderstand the question...
// Static
#ExtendWith(MockitoExtension.class)
#RunWith(PowerMockRunner.class)
#PrepareForTest(WebClient.class)
public class RasaServiceTest {
#Mock
private WebClient webClientMock;
#Mock
private WebClient.RequestHeadersSpec requestHeadersMock;
#Mock
private WebClient.RequestHeadersUriSpec requestHeadersUriMock;
#Mock
private WebClient.RequestBodyUriSpec requestBodyUriMock;
#Mock
private WebClient.ResponseSpec responseMock;
private MyService service = new MyService();
#BeforeAll
public void setup(){
PowerMockito.mockStatic(WebClient.class);
BDDMockito.given(WebClient.create(any())).willReturn(webClientMock);
}
#Test
public void HappyPath(){
MessageResponseItem item = new MessageResponseItem();
item.setRecipientId("id");
item.setResponseText("response");
MessageResponseItem[] items = {item};
when(webClientMock.post()).thenReturn(requestBodyUriMock);
when(requestHeadersUriMock.uri("/webhooks/rest/webhook")).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(MessageResponseItem[].class)).thenReturn(Mono.just(items));
InferenceEngineRequest request = new InferenceEngineRequest();
MessageResponseItem[] result = service.call(request).block();
assertThat(result.length).isEqualTo(1);
assertThat(result[0].getResponseText()).isEqualTo("response");
}
}
// Final
#RunWith(MockitoJUnitRunner.class)
public class RemoveMeConfigTest {
#Test
public void happyPath() throws Exception {
HttpSecurity security = Mockito.mock(HttpSecurity.class);
CsrfConfigurer<HttpSecurity> csrf = Mockito.mock(CsrfConfigurer.class);
RemoveMeConfig config = new RemoveMeConfig();
Mockito.when(
security.csrf()
).thenReturn(csrf);
Mockito.when(
csrf.disable()
).thenReturn(security);
config.configure(security);
Mockito.verify(
security,
Mockito.times(
1
)
).csrf();
Mockito.verify(
csrf,
Mockito.times(1)
).disable();
}
}
How to mock "System.getenv("...")" in JUnit.
Currently I am doing:
#RunWith(Parameterized.class)
#PowerMockRunnerDelegate(PowerMockRunner.class)
#PrepareForTest(System.class)
public class TestClass extends BaseTest {
public TestClass(String testCase) {
this.testCase = testCase;
}
#Before
#Override
public final void initTable() throws Throwable {
super.initTable();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
}
...
}
I am using both PowerMock and Parameterizedrunner.
I am getting below exception for line:
PowerMockito.when(System.getenv("ENV_VAR1")).thenReturn("1234");
Exception:
org.mockito.exceptions.base.MockitoException:
'afterPropertiesSet' is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
doThrow(exception).when(mock).someVoidMethod();
***
Use the #RunWith(PowerMockRunner.class) annotation at the class-level of the test case.
Use the #PrepareForTest({ClassThatCallsTheSystemClass.class}) annotation at the class-level of the test case.
Example with using EasyMock
public class SystemClassUser {
public String performEncode() throws UnsupportedEncodingException {
return URLEncoder.encode("string", "enc");
}
}
And test
#RunWith(PowerMockRunner.class)
#PrepareForTest( { SystemClassUser.class })
public class SystemClassUserTest {
#Test
public void assertThatMockingOfNonFinalSystemClassesWorks() throws Exception {
mockStatic(URLEncoder.class);
expect(URLEncoder.encode("string", "enc")).andReturn("something");
replayAll();
assertEquals("something", new SystemClassUser().performEncode());
verifyAll();
}
}
From:
https://github.com/powermock/powermock/wiki/MockSystem
So, you should add a class that uses the System.getenv, not the System class to #PrepareForTest.
This post explains why it should be done in such way.
Also, I'd like to recommend to use the System Rules library for your case. It has a good way to stub environment variables. PowerMock modifies a class byte code, so it makes test slowly. And even if it not modify a class it at least read class from disk.
I'm writing unit tests using Mockito and I'm having problems mocking the injected classes. The problem is that two of the injected classes are the same type, and only differentiated by their #Qualifier annotation. If I tried to simply mock SomeClass.class, that mock is not injected and that object is null in my tests. How can I mock these objects?
public class ProfileDAL {
#Inject
#Qualifier("qualifierA")
private SomeClass someClassA ;
#Inject
#Qualifier("qualifierB")
private SomeClass someClassB ;
//...various code, not important
}
#RunWith(MockitoJUnitRunner.class)
public class ProfileDALLOMImplTest {
#InjectMocks
private ProfileDALLOMImpl profileDALLOMImpl = new ProfileDALLOMImpl();
#Mock
private SomeClass someClassA;
#Mock
private SomeClass someClassB;
private SomeResult mockSomeResult = mock(SomeResult.class);
#Test
public void testSomeMethod() {
when(someClassA .getSomething(any(SomeArgment.class)).thenReturn(mockSomeResult);
Int result = profileDALLOMImpl.someTest(This isn't relevant);
}
}
I have tried mocking two objects with the same type with Mockito 1.9.5 using JUnit and it works.
See: http://static.javadoc.io/org.mockito/mockito-core/1.9.5/org/mockito/InjectMocks.html
Relevant type info from the doc:
"Field injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the field name and the mock name."
And this one which seems to say you should make the mock name match the field name for all your mocks when you have two of the same type:
"Note 1: If you have fields with the same type (or same erasure), it's better to name all #Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen."
Perhaps this latter one is biting you?
Just confirmed what Splonk pointed out and it works that way in Mockito 1.9.5, as soon as I removed one of the mocked classes, it failed.
So, in your case, make sure you have both of the mocked classes with the same name as in the class in your test:
#Mock
private SomeClass someClassA;
#Mock
private SomeClass someClassB;
If you don't use annotation, you get something like
public class MyClass {
private MyDependency myDependency;
public void setMyDependency(MyDependency myDependency){
this.myDependency = myDependency;
}
}
and
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.*;
public class MyTest {
private MyClass myClass;
private MyDependency myDependency;
#Before
public void setUp(){
myClass = new MyClass();
myDependency = mock(MyDependency.class);
myClass.setMyDependency(myDependency);
}
#Test
public void test(){
// Given
// When
// Then
}
}
You can do just the same if your object has its dependencies specified via constructor rather than via setter. I guess your dependency injection framework can annotate the setters the same way you annotate private fields, but now your tests don't rely on any dependency injection framework.