Mock Objects Created Inside Method Under Test to Verify the Arguments Passed

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.

  1. Then you use the @Captor ArgumentCaptor field of type QueryFiltersConfig to store our captured argument.

  2. Mockito.verify with the ArgumentCaptor to capture the QueryFiltersConfig and capture the argument using configCaptor.getValue()

  3. 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.

  1. The test does not expose clearly which object is being tested, and which interaction.
  2. This test is testing the implementation not behavior, that should be the opposite.
  3. Because of problem #1, mockito is misused

What that mean is that

  1. The test should show the case/scenario being tested, clear separations of the fixture, the invocation being tested, and the assertion/verification.
  2. 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.
  3. 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



Leave a reply



Submit