I am trying to test a method, but when my test method calls the actual method, due to #Value field present , the actual method always receives the value defined under #Value field i.e. null. You can have a look at the code for actual method and the test method below:
Actual method
public class IndexService {
#Value("${elasticsearch.index}")
private String index;
public boolean index(String id, String json, String index) {
try {
createIndex();
return true;
} catch (IOException e) {
log.warn("Exception in indexing data {}", e.getMessage());
}
return false;
}
private void createIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest(index);
}
}
Below is my test method:
#Test
public void IndexServiceIndex() throws IOException {
CreateIndexRequest request1 = new CreateIndexRequest(index);
request1.source("{\"name\":true}",XContentType.JSON);
Mockito.when(indicesClient.create(request1,RequestOptions.DEFAULT))
.thenReturn(createIndexResponse);
Boolean indexser = indexService.index("65","{\"name\":molly}","1");
}
Below is CreateIndexRequest class method:
public CreateIndexRequest(String index) {
if (index == null) {
throw new IllegalArgumentException("The index name cannot be null.");
} else {
this.index = index;
}
}
What happening is, when my test method calls the actual method indexService.index("65","{\"name\":molly}","1");, then the control goes to the actual method, and the private method createIndex is injecting index value which is defined above as #Value("${elasticsearch.index}") private String index;. and hence in CreateIndexRequest method , it always evaluates to null and throws exception IllegalArgumentException("The index name cannot be null.").
I tried using ReflectionTestUtils.setField but the required dependency of org.springframework.test.util.ReflectionTestUtils is not there in my project. Is there any other way to mock #Value field?
You simply don't. Using fields injection in general is discauraged because it makes testing code more complicated than it can be. To test whatever you try to test, use either
Constructor injection - you can #Value on constructor params, and it will be possible to put test value via constructor
setter injection - annotate setter method with #Value. It will work exactly the same in the container, and how to use it in tests is obvious
Use #TestProperties - but this will fix the values for the whole test class
Use reflection - this allows you to even mutate final fields, however if it comes to AOP and proxies, this might not simply work
and probably many others. I think that 1 and 2 are the most viable methods.
Related
I don't want to use powermock anymore. Because junit5 started mocking static classes. So i am trying to get rid of powermock methods.
As you know, you can create an instance of a class with whenNew keyword. So i decided to use " mockConstruction " . But mockConstruction does not return the mocked object. It doesn't go inside the try block.
This is my BeforeEach method:
#BeforeEach
void setUp() {
partUnlinkService =
spy(new PartVideoUnlinkService(part1, part2,
part3));
}
This is my test method:
#Test
void shouldThrowException() throws Exception {
//given
UrlLinkDto urlLinkDto =
UrlPartLinkDto.builder().callId("333").videoId("5555544").build();
ArgumentCaptor<UrlPartLinkDto> argumentCaptor = ArgumentCaptor.forClass(UrlPartLinkDto.class);
//when
try (MockedConstruction<ObjectMapper> ignoredVariable = mockConstruction(ObjectMapper.class,
(objectMapper, context) -> {
//then
partUnlinkService.unlink(urlLinkDto, false);
verify(partLogCheckService, times(1)).checkForExistingVideo(
urlLinkDto.getVideoId());
verify(objectMapper, times(1)).writeValueAsString(argumentCaptor.capture());
Throwable throwable =
catchThrowable(() -> objectMapper.writeValueAsString(argumentCaptor.capture()));
assertThat(throwable).isInstanceOf(JsonProcessingException.class);
})) {
}
}
Any help would be appreciated.
You can access mocks that were created during the instantiation of your objects via MockedConstruction.constructed() method. It represents a collection of mocks that are created after each constructor's execution. If your object will be instantiated 3 times MockedConstruction<T>.constructed() will return 3 different mock objects for each instantiation.
According to documentation MockedConstruction<T>
Represents a mock of any object construction of the represented type.
Within the scope of the mocked construction, the invocation of any
interceptor will generate a mock which will be prepared as specified
when generating this scope. The mock can also be received via this
instance.
Simple implementation with comments below shows how to get mocks from MockedConstruction and verify them:
public class A {
private final String test;
public A(String test) {
this.test = test;
}
public String check() {
return "checked " + this.test;
}
}
public class TestService {
public String purchaseProduct(String param) {
A a = new A(param);
return a.check();
}
}
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
public class ConstructorMockTest {
private MockedConstruction<A> mockAController;
#BeforeEach
public void beginTest() {
//create mock controller for all constructors of the given class
mockAController = Mockito.mockConstruction(A.class,
(mock, context) -> {
//implement initializer for mock. Set return value for object A mock methods
when(mock.check()).thenReturn(" Constructor Mock A ");
});
}
#Test
public void test() {
//each instantiation of class A will return new mock, which initialized by initializer from beginTest method
//new mock will be stored to mockAController.constructed() collection of mocks
A aObject = new A("test");
//ensure that method check() returns mocked value
Assertions.assertEquals(aObject.check(), " Constructor Mock A ");
//get just created mock for class A from controller. It will be first element of mockAController.constructed() collection
A aMock = mockAController.constructed().get(0);
//ensure that we get correct mock from mock controller, that it is equal from new created object
Assertions.assertEquals(aMock, aObject);
//verify that check method was executed on Mock
verify(aMock, times(1)).check();
//create new A object, new mock created and stored to mockAController.constructed()
A aObject2 = new A("test");
//ensure that method check() returns mocked value
Assertions.assertEquals(aObject2.check(), " Constructor Mock A ");
//get just created mock for class A from controller, it will be second object from constructed collection
A aMock2 = mockAController.constructed().get(1);
//ensure that we get correct mock from mock controller, that it is equal from just created A object
Assertions.assertEquals(aObject2, aMock2);
//verify that check method was executed on Mock
verify(aMock2, times(1)).check();
//Example of testing service which creates A object
TestService service = new TestService();
String serviceResult = service.purchaseProduct("test");
//ensure that service returned value from A mock
Assertions.assertEquals(serviceResult, " Constructor Mock A ");
//get just created mock for class A from controller, it will be third object from constructed collection
A aMock3 = mockAController.constructed().get(2);
//verify that check method was executed on Mock
verify(aMock3, times(1)).check();
}
#AfterEach
public void endTest() {
mockAController.close();
}
}
Let's rewrite your test. I do not have the full code, but I will try to create an example with comments:
#Test
void shouldThrowException() throws Exception {
//given
UrlLinkDto urlLinkDto =
UrlPartLinkDto.builder().callId("333").videoId("5555544").build();
ArgumentCaptor<UrlPartLinkDto> argumentCaptor = ArgumentCaptor.forClass(UrlPartLinkDto.class);
try (MockedConstruction<ObjectMapper> objectMapperMockedConstruction = mockConstruction(ObjectMapper.class,
(objectMapper, context) -> {
//initialize ObjectMapper mock to throw exception when argumentCaptor will come to writeValueAsString method
doThrow(JsonProcessingException.class).when(objectMapper).writeValueAsString(argumentCaptor.capture());
})) {
//execute service for testing and catch exception
Throwable throwable = catchThrowable(() -> partUnlinkService.unlink(urlLinkDto, false));
//verify that inner service was executed
verify(partLogCheckService, times(1)).checkForExistingVideo(urlLinkDto.getVideoId());
//verify that ObjectMapper was instantiated
Assertions.assertEquals(1, objectMapperMockedConstruction.constructed().size());
//get mock of ObjectMapper which was instantiated during service execution
ObjectMapper objectMapper = objectMapperMockedConstruction.constructed().get(0);
//verify that writeValueAsString was executed
verify(objectMapper, times(1)).writeValueAsString(argumentCaptor.capture());
//check that exception is correct
assertThat(throwable).isInstanceOf(JsonProcessingException.class);
}
}
I think I have the same question as you, hope someone can solve our problem.
In my case, I also want to got a return object like PowerMock.whenNew().withArguments().thenReturn(someThing), I try a lot of times but it failed.
I think mockConstruction just only can mock their behavior, can't verify their result like powerMock, and I couldn't find any articles or answers.
below is my post link :
Junit 5 use mockConstruction().withSetting().useConstructor() instead of PowerMock.whenNew().withArguments()
I am mocking 2 classes in one of my unit tests, defining the behavior with Mockito. When and then calling the functions.
One of the mocked classes works exactly as expected, the other returns null. I can't figure out what the difference is between the two.
QueryServiceTest.java
#Import({ QueryServiceTestConfig.class })
#RunWith(SpringRunner.class)
public class QueryServiceTest {
#Autowired
private QueryService queryService;
#MockBean
private ElasticConnectionService elasticConnectionService;
#MockBean
private HBaseConnectionService hbaseConnectionService;
#Test
public void test_getRecordsFromQuery() throws IOException {
// creation of sample data for inputs and outputs goes here
// This mock works when called from queryService.getRecordsFromQuery()
when(elasticConnectionService.getRowIdsFromQuery(filterParams, testIndex)).thenReturn(getRowIdsFromQuery_result);
List<JSONObject> matches = queryService.getMatchingRowIds(getRowIdsFromQuery_result);
// matchesArray is directly defined to make sure its exactly the same as in queryService.getRecordsFromQuery()
JSONObject matchesArray = new JSONObject("{\"testTable\":[\"testUUID\"]}");
// This mock fails when called from queryService.getRecordsFromQuery()
when(hbaseConnectionService.getRowsByIDs(matchesArray)).thenReturn(getRowsByIDs_result);
// This returns getRowsByIDs_result as expected
JSONArray test = hbaseConnectionService.getRowsByIDs(matchesArray);
// This returns null
JSONArray actual = new JSONArray(queryService.getRecordsFromQuery(filterParams, testIndex));
}
}
QueryService.java
#Service
public class QueryService {
#Autowired
private ElasticConnectionService elasticConnectionService;
#Autowired
private HBaseConnectionService hbaseConnectionService;
#Autowired
private PSQLConnectionService psqlConnectionService;
public String getRecordsFromQuery(
Map<String,String> filterParams,
String tablename) throws IOException {
/**
* Get records that match simple key/value filters
*/
// This mocked method returns exactly what was expected
List<List<JSONObject>> lookupsList = elasticConnectionService.getRowIdsFromQuery(filterParams, tablename);
List<JSONObject> matches = getMatchingRowIds(lookupsList);
// matchesArray is exactly the same as in the test class
JSONObject matchesArray = new JSONObject("{\"testTable\":[\"testUUID\"]}");
// This returns null
JSONArray hbResults = hbaseConnectionService.getRowsByIDs(matchesArray);
return hbResults.toString(4);
}
}
QueryServiceTestConfig.java
#Configuration
public class QueryServiceTestConfig {
#Bean
public QueryService queryService() {
return new QueryService();
}
#Bean
public ElasticConnectionService elasticConnectionService() {
return new ElasticConnectionService();
}
#Bean
public HBaseConnectionService hbaseConnectionService() {
return new HBaseConnectionService();
}
#Bean
public PSQLConnectionService psqlConnectionService() {
return new PSQLConnectionService();
}
}
What confuses me most is that in queryService.getRecordsByQuery(), the elasticConnectionService.getRowIDsFromQuery() mock returns what was expected, but the hbaseConnectionService.getRowsByIDs() mock returns null.
The elastic and hbase connection service classes are both defined in the same folder and the only annotation they have is #Service. I would think I had configured something wrong if both failed, but the fact that the elasticConnectionService call works as expected tells me something else is happening.
If the package of JSONObject is org.json, JSONObject's equals method looks like:
public boolean equals(Object object) {
return object == null || object == this;
}
Since the instance of matchesArray in QueryService is different than the instance in QueryServiceTest, the equals() method will return false.
Try changing this:
when(hbaseConnectionService.getRowsByIDs(matchesArray)).thenReturn(getRowsByIDs_result);
to this, and see if your results change:
when(hbaseConnectionService.getRowsByIDs(Mockito.any())).thenReturn(getRowsByIDs_result);
I think you also may be able to do this:
when(hbaseConnectionService.getRowsByIDs(Mockito.eq(matchesArray))).thenReturn(getRowsByIDs_result);
or:
when(hbaseConnectionService.getRowsByIDs(Matchers.eq(matchesArray))).thenReturn(getRowsByIDs_result);
Because under the hood, the Matchers.eq() method probably calls JSONObject.equals(), the Matcher probably won't work (I didn't check the source code for Matchers.eq()).
In general, when setting up a mock method call, you want to wrap your parameter(s) in one of Mockito's Matcher's methods. Unfortunately, that won't work in your scenario.
(Note that the class Mockito extends Matchers)
I'm using Java 8 Functions and converters and have the following:
Main class
public final class MainClass {
public MainClass(
final Function<InputModel, OutputModel> businessLogic,
final Converter<Input, InputModel> inputConverter,
final Converter<OutputModel, Output> outputConverter) {
this.businessLogic = requireNonNull(businessLogic, "businessLogic is null.");
this.inputConverter = requireNonNull(inputConverter, "inputConverter is null.");
this.outputConverter = requireNonNull(outputConverter, "outputConverter is null.");
}
/**
* Request Handler.
*/
public Output handleRequest(final Input input) {
requireNonNull(input, "input is null.");
log.info("input request: {}", input);
try {
return inputConverter
.andThen(businessLogic)
.andThen(outputConverter)
.apply(input);
} catch (final Exception ex) {
throw new InternalServiceException(ex.getMessage(), ex);
}
}
}
Unit test
public final class TestClass {
#Mock
private Function<InputModel, OutputModel> mockedBusinessLogic;
#Mock
private Converter<Input, InputModel> mockedInputConverter;
#Mock
private Converter<OutputModel, Output> mockedOutputConverter;
private MainClass mainClass = new MainClass(mockedBusinessLogic, mockedInputConverter, mockedOutputConverter);
#Test
public void handleRequest_SHOULD_throwException_WHEN_inputConverter_throwsException() {
final RuntimeException inputConverterException = new NullPointerException(EXCEPTION_MESSAGE);
// what should I mock here? apply or convert.. apply for `Converter` seems to be deprecated.
when(mockedInputConverter.convert(input))
.thenThrow(inputConverterException);
final Exception exception = assertThrows(
InternalServiceException.class,
() -> mainClass.handleRequest(input)
);
assertThat(exception.getMessage(), is(EXCEPTION_MESSAGE));
assertThat(exception.getCause(), is(inputConverterException));
}
}
The above assertions fail.
I expect that if the inputConverter throws an exception, the catch block in handleRequest would wrap it to InternalServiceException, but it doesn't seem to be happening.
Any help?
How do I actually write unit tests for handleRequest method? I want to test the behavior when either of inputConveter, businessLogic or outputConveter throws exception.
Everything in your code is a mock. When you call andThen on your mocked inputConverter, then either null or a new mock instance is returned (depending on configuration). Each andThen will return a new instance with the chained converters (at least that is what I assume)
Make sure you mock all required methods, or better, use real objects instantiated from real classes.
Setting breakpoints and then debugging should help you find the issue. If you set in your try-block, and then single-step through the code, you will see that the way mocks are used in your code will not work. You could also save each result of andThen in a variable and then check in the debugger what type each has. I'm pretty sure it will either be null or "Mock for class X".
My application uses Google Guice dependency injection framework. Im now having trouble figuring out a way to write unit tests for my class.
private final Person aRecord;
#Inject
public MyClass(Venue venue, #Assisted("record") Record myRecord) {
super(venue,myRecord);
aRecord = (Person) myRecord;
}
public void build() throws Exception {
super.build();
super.getParentRecord().setJobType(aRecord.getJobType());
super.getParentRecord().setHairColor(aRecord.getHairColor());
super.getParentRecord().setEyeColor(aRecord.getEyeColor());
}
I want to write a unit test for the build() method in the child class, it should
Ensure that when super.build() gets called all of the basic information on the super.getParentRecord() is populated e.g. age, gender etc.
Ensure an exception is thrown in the build() method if aRecord.getJobType() is null
Ensure an exception is thrown in the build() method if aRecord.getHairColor() is null
Ensure an exception is thrown in the build() method if aRecord.getEyeColor() is null
You have two dependencies of MyClass (Venue and Record) so you have to mock those.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
...
Venue venueMock = mock(Venue.class);
Record recordMock = mock(Record.class);
Then in your unit test you have to create an instance of MyClass and assert the expected result:
For example: "Ensure an exception is thrown in the build() method if aRecord.getJobType() is null"
#Test(expected=RuntimeException.class)// or whatever exception you expect
public void testIfExceptionIsThrownWhengetJobTypeReturnsNull() throws Throwable {
Venue venueMock = mock(Venue.class); //create the mocks
Record recordMock = mock(Record.class);//create the mocks
when(recordMock.getJobType()).thenReturn(null); //specify the behavior of the components that are not relevant to the tests
MyClass myClass = new MyClass(venueMock, recordMock);
myClass.build();
//you can make some assertions here if you expect some result instead of exception
}
Note that if you don't specify the return value of any of the mocked dependencies (with when()) it will return the default value for the return type - null for objects, 0 for primitive numbers, false for boolean, etc. So it will be best to stub all of the methods that are used in MyClass.
I have a class that contains the following method, which I do not expect to ever return null:
public String getAutoSaveDir() {
if (autoSaveDir == null || autoSaveDir.isEmpty()) {
return System.getProperty("java.io.tmpdir");
} else {
return autoSaveDir;
}
}
I need to mock this class with #Injectable for other reasons.* Unfortunately, this means that this method returns null:
#Tested
private MyClass myClass;
#Injectable
private ClassContainingMethod classContainingMethod;
#Test
public void myTest() {
// Calls classContainingMethod.getAutoSaveDir()
myClass.saveFile();
}
This unfortunately fails, as getAutoSaveDir() returns null. I confirmed that System.getProperty("java.io.tmpdir") does not return null, so it seems that JMockit is mocking the method to return null, when I don't want it to.
I am aware that I can set up an Expectations block to tell it to just return System.getProperty("java.io.tmpdir"), but it seems really silly to mock it to return something that it would be returning anyway if unmocked.
How can I have JMockit not mock this method?
I am using JMockit 1.22 (would love to use a later version, but we have several instances of mocking private methods, which isn't allowed in later versions).
* I have to use #Injectable because the field is annotated with #Autowired. If I try to use a real instance, I get this error: java.lang.IllegalStateException: Missing #Injectable for field MyClass#classContainingMethod, of type com.example.ClassContainingMethod