I need to mock the behavior of a static method inside a non-static factory class. The implementation of the class is:
ABCFactory.java
public class ABCFactory extends BaseUserFactory
{
private static final ABCFactory factory = new ABCFactory();
public static final ABCFactory getFactory()
{
return factory;
}
public Context getContext(String authority)
{
return (Context)createInstance(authority);
}
private ABCFactory()
{
}
protected Class getInterface()
{
return ABCFactory.class;
}
}
Now, this class is used in my code to get the profile something like:
Document.java:
Profile profile = ABCFactory.getFactory().getContext(authority).currentProfile();
I need to mock the ABCFactory class so that I can send my own context/profile object as a return type while testing. I've tried a bunch of methods but nothing seems to work here. Here's what I tried in my junit test class.
Try 1:
DocumentTest.java
ABCFactory mockABCFactory = Mockito.mock(ABCFactory.class);
ServiceProviderRegistrar.getRegistrar().bind(ABCFactory.class).toMockInstance(mockABCFactory);
Mockito.when(mockABCFactory .getFactory()).thenReturn(null);
Mockito.when(mockABCFactory .getContext(domain)).thenReturn(null);
Error:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
Try 2: (Using PowerMock to avoid the new call.
DocumentTest.java
ABCFactory mockABCFactory = Mockito.mock(ABCFactory.class);
ServiceProviderRegistrar.getRegistrar().bind(ABCFactory.class).toMockInstance(mockABCFactory);
try
{
PowerMockito.whenNew(ABCFactory.class).withNoArguments().thenReturn(mockABCFactory);
PowerMockito.when(ABCFactory.getFactory()).thenReturn(mockABCFactory);
}
catch (Exception e)
{
e.printStackTrace();
}
Mockito.when(mockABCFactory.getContext(domain)).thenReturn(null);
Error:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
at org.powermock.api.mockito.PowerMockito.when(PowerMockito.java:490)
Try 3: (Used PowerMock.mockStatic)
DocumentTest.java
ABCFactory mockABCFactory= Mockito.mock(ABCFactory.class);
ServiceProviderRegistrar.getRegistrar().bind(ABCFactory.class).toMockInstance(mockABCFactory);
try
{
PowerMockito.whenNew(ABCFactory.class).withNoArguments().thenReturn(mockABCFactory);
PowerMockito.mockStatic(ABCFactory.class);
PowerMockito.when(ABCFactory.getFactory()).thenReturn(mockABCFactory);
}
catch (Exception e)
{
e.printStackTrace();
}
Mockito.when(mockABCFactory.getContext(domain)).thenReturn(null);
Error:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
at org.powermock.api.mockito.PowerMockito.when(PowerMockito.java:490)
What am I missing here. I have tried several other ways but ABCFactory.getFactory() always returns a new object but not my mocked object. How do I mock the behavior of ABCFactory class without changing its implementation?! Please help.
Did you use following annotations.
#RunWith(PowerMockRunner.class)
#PrepareForTest( ABCFactory.class )
I tried and following code works.
DocumentTest.class
#RunWith(PowerMockRunner.class)
#PrepareForTest( ABCFactory.class )
public class DocumentTest
{
/** Unit under test. */
private Document user;
#Before public void setUp() {
user = new Document();
ABCFactory abc = ABCFactory.getFactory();
PowerMockito.mockStatic(ABCFactory.class);
PowerMockito.when(ABCFactory.getFactory()).thenReturn(abc);
}
#Test public void testABC() {
assertEquals("", user.useFactory() );
}
}
Document class
public class Document
{
public String useFactory(){
String profile = ABCFactory.getFactory().getContext("");
return profile;
}
}
Related
I have a Helidon application and I would like to test (part of) it.
My test is annotated with #HelidonTest, and now I would like to replace one bean by a mock (and configure this mock, use all other beans as they are found, but with the mock injected).
I did figured out how to:
Replace one bean by a test implementation (separate class): By annotating the test implementation class with #Priority(1) and #Alternative and supply it by annotating the test with #AddBean(MyBeanTestImpl.class).
But I can not create a mock (with Mockito) as an individual class.
Produce a mock(MyBean.class): By creating a producer method and annotate it with #Produces:
But it clashes with the real bean and gives: "WELD-001409: Ambiguous dependencies for type..."
When I annotate it also with #Alternative it is simply ignored.
I can not annotate it with #Priority(1), because this annotation can only be applied to types and parameters.
Any idea how I can replace one bean by a mock?
I tried setter injection to manually inject mock beans.
Class under test
#ApplicationScoped
public class SomeService {
private ExternalService externalService;
#Inject
public void setExternalService(ExternalService externalService) {
this.externalService = externalService;
}
public String doSomething() {
return externalService.getData();
}
}
Test Class
#HelidonTest
class SomeServiceTest {
#Inject
private SomeService someService;
private ExternalService externalService;
#BeforeEach
void setUp() {
externalService = Mockito.mock(ExternalService.class);
someService.setExternalService(externalService);
}
#Test
void doSomething() {
Mockito.when(externalService.getData())
.thenReturn("Mock data");
String actual = someService.doSomething();
Assertions.assertEquals("Mock data", actual);
}
}
There are also methods to mock a whole bean by mocking the constructor as well. For that, we have to make use of #Observes annotation
#HelidonTest
public abstract class HelidonTestHelper {
private MockedConstruction<ExternalService> mockedConstruction;
void init(#Priority(1) #Observes #Initialized(ApplicationScoped.class) ContainerInitialized containerInitialized) {
mockedConstruction = Mockito.mockConstruction(ExternalService.class);
//mock common beans here. This will be executed once application scope is loaded.
}
void onStop(#Priority(1) #Observes #Destroyed(ApplicationScoped.class) ContainerShutdown containerShutdown) {
//do cleanup here if needed.
mockedConstruction.closeOnDemand();
}
}
Once the above is done, instead of helidon test, you can extend the helper class we created.
class SomeServiceTest extends HelidonTestHelper {
#Inject
private SomeService someService;
#Inject //this will be a mock
private ExternalService externalService;
#Test
void doSomething() {
Mockito.when(externalService.getData())
.thenReturn("Mock data");
String actual = someService.doSomething();
Assertions.assertEquals("Mock data", actual);
}
}
I wanted to mock static methods, hence used dependency "mockito-inline" 3.8 version instead of "mockito-core"
The static method mocks work fine but my old tests that mock interfaces fail with the below error
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to when() is not a mock!
Example of correct stubbing:
doThrow(new RuntimeException()).when(mock).someMethod();
Reverting to using mockito-core solves the issue but then I would not be able to mock static methods
Is there any way we can choose different mockito engines (Subclass/Inline) for each class?
MockResolver fixed the issue as described in https://github.com/mockito/mockito/issues/1980
If you need to mock static methods and you cannot use mockito-inline you could do something like this with mockito-core:
public class MockitoUtil {
public static <T, S> void mockStaticMethod(Class<T> classToMock, String nameOfMethodToMock, S result) {
MockCreationSettings<T> settings = mock(MockCreationSettings.class);
when(settings.isSerializable()).thenReturn(true);
MockHandler<T> handler = new MockHandler<>() {
private static final long serialVersionUID = 1L;
#Override
public Object handle(Invocation invocation) throws Throwable {
String invokedMethodName = invocation.getMethod().getName();
if(!invokedMethodName.equals(nameOfMethodToMock)){
String message = "called %s on class %s, but only %s was implemented";
throw new Exception(String.format(message, invokedMethodName, classToMock.getName(), nameOfMethodToMock));
}
return result;
}
#Override
public MockCreationSettings<T> getMockSettings() { return null; }
#Override
public InvocationContainer getInvocationContainer() { return null; }
};
MockMaker.StaticMockControl<T> classToMockControl = Mockito
.framework()
.getPlugins()
.getInlineMockMaker()
.createStaticMock(classToMock, settings, handler);
classToMockControl.enable();
}
}
N.B. you should call classToMockControl.disable() after the static method invocation if you'd like to mock static methods of other classes in the same test
adding test cases for getStudent method, this is having internal calls
1- is repository call - stubbing this call working fine
2- validate user call - stubbing this call not working, showing some error and test case failed.
Service Class
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
FakeStudentRepository fakeStudentRepository;
#Override
public Optional<Student> getStudent(int id) {
Optional<Student> student = fakeStudentRepository.getStudent(id);
boolean isValid = myClass().isValidUser(student.get().getId());
if(!isValid) {
return Optional.empty();
}
return student;
}
public MyTestClass myClass() {
return new MyTestClass();
}
}
MyTestClass
public class MyTestClass {
public boolean isValidUser(int id) {
return true;
}
}
Test Class
#SpringBootTest
class StudentServiceImplTest {
#Mock
FakeStudentRepository fakeStudentRepository;
#InjectMocks
StudentServiceImpl studentServiceImpl;
#BeforeEach
public void setup() {
studentServiceImpl = Mockito.spy(StudentServiceImpl.class);
MockitoAnnotations.initMocks(this);
}
#Test
void getStudent() {
Optional<Student> student = Optional.of(Student.builder().id(1).firstName("Rahul").lastName("rahul")
.mobile("XXXXXX").build());
Mockito.doReturn(student)
.when(fakeStudentRepository).getStudent(ArgumentMatchers.anyInt());
Mockito.doReturn(false)
.when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());
Optional<Student> resultStudent = studentServiceImpl.getStudent(student.get().getId());
assertEquals(resultStudent.get().getId(), student.get().getId());
}
}
Error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: Boolean
cannot be returned by myClass() myClass() should return MyTestClass
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
The error Message says it: You are mocking studentServiceImpl.myClass() and try to return true. It’s not possible to mock the end of a call chain as you try with your second Mockito expression.
To do what you want requires to mock myClass() first by returning a mocked class instance and mock isValidUser on that.
I have class Name Validator and it has a method forVote.
This is my code.
public function test_should_set_default()
{
$this->mock = \Mockery::mock(Validator::class);
$this->mock->shouldReceive('forVote')
->andReturnTrue();
$this->app->instance(Validator::class,$this->mock);
$factory = new Factory();
$this->assertTrue($factory->setDefault());
}
So Factory calls Processor which calls Validator. Now I want mock validator to run. But it calls the real method.
What am I doing wrong?
https://laravel.com/docs/5.6/container#introduction
since the repository is injected, we are able to easily swap it out
with another implementation. We are also able to easily "mock", or
create a dummy implementation of the UserRepository when testing our
application.
My guess is you are perhaps currently instantiating your dependencies like so:
$processor = new Processor() and $validator = Validator::make(...);
So, in order to have your mocked class be used, you should use Dependency injection which just means your classes should inject your dependencies via the __construct method.
Your Factory class should be like:
class Factory {
$processor;
public function __construct(Processor $processor)
{
$this->processor = $processor;
}
public function setDefault()
{
$this->processor->callingValidator();
}
}
and your Processor to be like:
class Processor {
$validator;
/**
* The Validator will resolve to your mocked class.
*
*/
public function __construct(Validator $validator)
{
$this->validator = $validator;
}
public function callingValidator()
{
$this->validator->make();
}
}
I have some code that has a static method inside a final class. I was trying to mock that method. I have tried doing a few things..
public final Class Class1{
public static void doSomething(){
}
}
How can I mock doSomething()? I have tried..
Class1 c1=PowerMockito.mock(Class1.class)
PowerMockito.mockSatic(Class1.class);
Mockito.doNothing().when(c1).doSomething();
This gives me an error:
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.cerner.devcenter.wag.textHandler.AssessmentFactory_Test.testGetGradeReport_HTMLAssessment(AssessmentFactory_Test.java:63)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:260)
Most used testing framework is JUnit 4. So if you are using it you need to annotate test class with:
#RunWith( PowerMockRunner.class )
#PrepareForTest( Class1.class )
Than
PowerMockito.mockSatic(Class1.class);
Mockito.doNothing().when(c1).doSomething();
Mockito.when(Class1.doSomething()).thenReturn(fakedValue);
// call of static method is required to mock it
PowerMockito.doNothing().when(Class1.class);
Class1.doSomething();
I use PowerMock. It let's you do things Mockito can't do.
https://github.com/powermock/powermock/wiki
#RunWith(PowerMockRunner.class)
#PrepareForTest(StaticClass.class)
public class StaticTest {
#Before
public void setUp() {
PowerMockito.mockStatic(Bukkit.class);
//When a static method with no arguments is called.
when(StaticClass.callMethod1()).thenReturn(value);
//When a static method with an argument is called.
when(StaticClass.callMethod2(argument)).thenReturn(value2);
//Use when you don't care what the argument is..
//use Mockito.anyInt(), Mockito.anyDouble(), etc.
when(StaticClass.callMethod3(Mockito.anyString())).thenReturn(value3);
}
#Test
public void VerifyStaticMethodsWork() {
assertEquals(value, StaticClass.callMethod1());
assertEquals(value2, StaticClass.callMethod2(argument));
assertEquals(value3, StaticClass.callMethod3("Hello"));
}
}