How to mock an object which is created inside a method which is under test only with mockito not with powermockito?
Sample
is tightly coupled to Second
by creating it within the method under test.
Given the currently stated limitations,
And I need to be done only with mockito not with powermockito
Mockito will not be able to mock the Second
used within the Sample
.
If Sample
can be refactored to follow a more SOLID design like Explicit Dependency Principle via constructor injection.
public class Sample {
private final Second second;
public Sample(Second second) {
this.second = second;
}
public String sampleMethod(){
return s.show("This should not be displayed");
}
}
Then the dependency would be able to be mocked and injected into the subject class under test
public class SampleTest {
@Mock
Second second;
@InjectMocks
Sample sample; //second will be injected here
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void sampleMethodTest() {
//Arrange
String expected = "This should be displayed";
when(second.show(any(String.class))).thenReturn(expected);
//Act
String actual = sample.sampleMethod();
//Assert
assertEquals(expected, actual);
}
}
Note the changes to the test so that it behaves as expected when exercised.
If you can't refactor Sample
, powerMockito can mock the class initialization.
While powerMockito allows such things, it facilitates poor design choices and code smells like tight coupling, which makes the code difficult to maintain and to test in isolation.
How to write a unit test for method that creates an object inside of it and passes it to mocked object?
Use the ArgumentCaptor
that allows you to capture an argument passed to a method in order to inspect it. This is especially useful when you can't access the argument outside of the method we'd like to test. Here is a good reference.
Let me try to give an example based on your method. I am assuming you have a service called YourService
which you can use @InjectMocks
annotation to inject the mocked CertificateRepository
.
Then you use the
@Captor
ArgumentCaptor field of typeQueryFiltersConfig
to store our captured argument.Mockito.verify
with the ArgumentCaptor to capture theQueryFiltersConfig
and capture the argument usingconfigCaptor.getValue()
Inspect the Captured Value using an assert.
@RunWith(MockitoJUnitRunner.class)
public class CertificateRepositoryTest {
@Mock
CertificateRepository certificateRepository;
@InjectMocks
YourService yourService;
// 1
@Captor
ArgumentCaptor<QueryFiltersConfig> configCaptor;
@Test
public void testWithCaptor() {
// 2 - call your service and let it do whatever it needs
// with the parameter passed to it
yourService.fetchCertificatesWithFilters(parameters);
// 2 - capture the object created inside your service
Mockito.verify(certificateRepository)
.findWithFilters(configCaptor.capture());
QueryFiltersConfig value = configCaptor.getValue();
// 3 - and make sure that the value is correct
assertEquals(expectedValue, value);
}
}
How to mock method calls of some object which is passed as method parameter of method under JUnit Test
public class B2 extends B { // so, this is, in fact, an IS-A of B
public String method2() {
return "mockedResult";
}
}
In your test, you'll now have something like this:
@Test
public void testMethod() {
A toTest = new A();
assertEquals("mockedResult", toTest.myMethod(new B2());
}
This is the very simplistic way. But I would recommend to read up on a Mocking framework, such as Mockito for more complex scenario's.
https://www.baeldung.com/mockito-series
By using Mockito, for short, it would be something like this:
public class TestClass {
@Mock
private B b;
private A toTest = new A();
@Test
public void testMethod() {
when(b.method2()).thenReturn("mockedResult");
assertEquals("mockedResult", toTest.myMethod());
}
}
Verifying objects within a mocked object are passed into argument in Mockito
This code has a design issue that makes it difficult to test with mocks.
- The test does not expose clearly which object is being tested, and which interaction.
- This test is testing the implementation not behavior, that should be the opposite.
- Because of problem #1, mockito is misused
What that mean is that
- The test should show the case/scenario being tested, clear separations of the fixture, the invocation being tested, and the assertion/verification.
- The test should test behavior, that is interactions between the tested objects and collaborators, not internal (as it may change without breaking the test). Also the test can test an expected output given an input.
- If #1 and #2 are addressed then it is obvious which type has to be mocked, and to follow tutorials here and there.
Here's an idea of how I'd write the test, this code is mainly focused on interactions, but it is possible to focus the assertions on the state of Class
(don't mock them in this case !!!) :
@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
@Mock Class class1;
@Mock Class class2;
@Test public void ensure_that____whatever() {
// given
WrapperUtils tested_utils = new WrapperUtils(new WrapperClass(class1, class2));
// when
tested_utils.processClass();
// then
verify(class1).someInteraction();
verify(class2).someInteraction();
}
}
And the implementation could look like
public class WrapperUtils {
private WrapperClass wrapperClass;
public WrapperUtils(WrapperClass wrapperClass) {
this.wrapperClass = wrapperClass;
}
public void processClasses() {
doSomething(wrapperClass.class1);
doSomething(wrapperClass.class2);
}
public void doSomething(Class clazz) {
clazz.someInteraction();
}
}
Note the wrapperClass
is injected in the WrapperUtils
via the constructor, that works but you can also pass a Supplier
(available in guava or in JDK8), this supplier could get the data from anywhere, a webservice for exemple. Or it could your type. In the test the supplier would be a mock.
@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
@Mock Class class1;
@Mock Class class2;
@Mock Supplier<WrapperClass> wrapped_class_supplier;
@Test public void ensure_that____whatever() {
// given
BDDMockito.given(wrapped_class_supplier.get()).willReturn(new WrapperClass(class1, class2));
WrapperUtils tested_utils = new WrapperUtils(wrapped_class_supplier);
// when
tested_utils.processClass();
// then
verify(class1).someInteraction();
verify(class2).someInteraction();
}
}
I strongly advice you to follow Test Driven Development methodology it really helps to write good software. Also there's this book Growing Object Oriented Software Guided by Tests that is a great read, the book may seem old but it is still describes best practices.
Related Topics
Java Socket Api: How to Tell If a Connection Has Been Closed
Returning Json Object as Response in Spring Boot
In Java, How to Determine If a Char Array Contains a Particular Character
Checking If a Document Exists in a Firestore Collection
Spring Data JPA and Exists Query
Spring Boot Rest API Returns 404
Calculate the Number of Items Displayed by Recyclerview and Place in a Textview
How Would You Make a !!Say Command for a Discord Bot
Eclipse: the Resource Is Not on the Build Path of a Java Project
Spring Junit: How to Mock Autowired Component in Autowired Component
How to Convert Minutes to Hours and Minutes (Hh:Mm) in Java
Retrieving Data from Biometric Fingerprint Attendance Device
Is There a Simpler Way to Check Multiple Values Against One Value in an If-Statement
How to Open a .Dat File in Java Program
How to Return a Select Query from JPA Repository With Foreign Key Type