#ReplaceWithMock not mocking using Springmockito-annotations - java

I have a class which I am trying unit test. I am trying to test it using Mockito, and to resolve the spring injection with mockito I am using Springockito and Springockito-annotations.
#ContextConfiguration(loader = SpringockitoContextLoader.class,
locations = {"classpath:testApplication-context-EU.xml"})
public class RelatedSearchToHotUrlProcessorTest extends AbstractJUnit4SpringContextTests {
#Autowired
RelatedSearchToHotUrlProcessor processor;
#ReplaceWithMock
private RestOperations restTemplate;
#Test
public void testGetCategoryFromIdWithNoStoredAlias() {
Taxonomy mockTaxonomy = mock(Taxonomy.class, RETURNS_DEEP_STUBS);
GraphType.Node mockNode = mock(GraphType.Node.class);
when(restTemplate.getForObject(anyString(), eq(Taxonomy.class))).thenReturn(mockTaxonomy);
when(mockTaxonomy
.getRev()
.get(0)
.getCountry()
.get(0)
.getGraph()
.getNodeOrAtom()
.get(0)).thenReturn(mockNode);
when(mockNode.getAlias()).thenReturn("mockalias");
String categoryAlias = processor.getCategoryAliasFromId(13130L);
assertEquals("mockalias", categoryAlias);
}
}
If I remove the #ReplaceWithMock and the private RestOperations restTemplate lines then it makes the right call and the value can be validated as correct. However, I want to mock the RestOperations object inside the processor, but using the #ReplaceWithMock makes the restTemplate variable null, causing it to fail. I haven't been able to work out how to isolate this member and mock it.

Was having a similar problem, I found that annotating with #WrapWithSpy or #ReplaceWithMock was not enough. That is to say the field in the test class was null. Adding the #Autowired annotation in addition to the springockito annotation as per Arasu's comment fixed the problem - although it does look weird...
#Autowired
#WrapWithSpy
private SomeBean someBean;

Related

Spring Boot Test : Mix up #MockBean and #SpyBean in same Test class on same Bean object?

Here a test code sample with Spring Boot Test:
#SpringBootTest
#AutoConfigureMockMvc
class SendMoneyControllerSpringBootSpyBeanTest {
#Autowired
private MockMvc mockMvc;
#SpyBean
private SendMoneyUseCase sendMoneyUseCase;
#Test
void testSendMoneyWithMock() {
...
}
#Test
void testSendMoneyWithSpy() {
...
}
}
Now suppose the two test methods like in the snippet above. One is using the spy version of the spring bean, whereas the other is using a mock version of the spring bean. How can I mix up both and distinguish them in my test methods ?
For example, can I do :
#SpringBootTest
#AutoConfigureMockMvc
class SendMoneyControllerSpringBootSpyBeanTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SendMoneyUseCase sendMoneyUseCaseMocked;
#SpyBean
private SendMoneyUseCase sendMoneyUseCaseSpied;
}
I know that in spring bean container, there is only one of sendMoneyUseCaseMocked or sendMoneyUseCaseSpied because they are the same java type. I can use a qualifier, to refer them by name. But in that case, how do I write my mock condition in the corresponding test method (write either mock condition on the mocked bean or the spied condition on the spied bean in the concerned test method).
EDIT : Another approach is to remove the line of code #MockBean, like this, the spied method is working. Consequently, I need then to programmatically code #MockBean in the mocked test method with Spring boot API, but how to do ?.
Thx.
I see two solutions.
Either use two test classes. One with the #MockBean object and one with the #SpyBean object. Both can inherit from the same abstract parent-class.
Let Spring Boot inject #MockBean and then replace it manually in the testSendMoneyWithSpy() test with the #SpyBean object using reflection.
But, why do you want to use the spy-object at all?
If you make unit-tests, you should mock the service and only test the controller here. And test the SendMoneyUseCase class in the SendMondeyUseCaseTest test.
(btw. strange name SendMondeyUseCase why not SendMoneyService or SendMoneyComponent. Or MoneySendService?)
Here the solution, first the solution code (my real test case, the one in first topic is more like a study code) :
#Slf4j
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class DocumentsApiTest extends ProductAPITestRoot {
#Autowired
private TestRestTemplate restTemplate;
#SpyBean
private AlfrescoService alfrescoServiceSpy;
#MockBean
private CloseableHttpClient closeableHttpClient;
#Test
public void testUploadDocument_SUCCESS() {
HttpHeaders headers = createOKHttpHeaders();
DocumentWithFile documentWithFile = createDocumentWithFile();
doReturn(StringUtils.EMPTY).when(alfrescoServiceSpy).sendDocument(anyString(), any(InputStream.class), eq(documentWithFile.getNomFichier()));
HttpEntity<DocumentWithFile> request = new HttpEntity<>(documentWithFile, headers);
ResponseEntity<Long> response = restTemplate.exchange(
"/documents/upload", HttpMethod.POST, request, Long.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo(1L);
}
#Test
public void testUploadDocument_500_INTERNAL_SERVER_ERROR() {
HttpHeaders headers = createOKHttpHeaders();
DocumentWithFile documentWithFile = createDocumentWithFile();
doThrow(new AlfrescoServiceException("Alfresco has failed !")).when(alfrescoServiceSpy).sendDocument(anyString(), any(InputStream.class), eq(documentWithFile.getNomFichier()));
HttpEntity<DocumentWithFile> request = new HttpEntity<>(documentWithFile, headers);
ResponseEntity<String> response = restTemplate.exchange(
"/documents/upload", HttpMethod.POST, request, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(response.getBody()).contains("Internal Server Error");
}
#Test
public void testUploadDocument_502_BAD_GATEWAY() throws IOException {
HttpHeaders headers = createOKHttpHeaders();
DocumentWithFile documentWithFile = createDocumentWithFile();
CloseableHttpResponse alfrescoResponse = mock(CloseableHttpResponse.class);
when(closeableHttpClient.execute(any(HttpPost.class))).thenReturn(alfrescoResponse);
when(alfrescoResponse.getStatusLine()).thenReturn(new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.BAD_GATEWAY.value(), "FINE!"));
HttpEntity<DocumentWithFile> request = new HttpEntity<>(documentWithFile, headers);
ResponseEntity<String> response = restTemplate.exchange(
"/documents/upload", HttpMethod.POST, request, String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_GATEWAY);
assertThat(response.getBody()).contains("Erreur Alfresco");
}
private static HttpHeaders createOKHttpHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + OK_TOKEN);
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
private static DocumentWithFile createDocumentWithFile() {
String fileAsString = RandomStringUtils.randomAlphabetic((int) 1e6);
byte[] fileAsStringBase64 = Base64.getEncoder().encode(fileAsString.getBytes());
DocumentWithFile documentWithFile = new DocumentWithFile();
String nomFichierExpected = "nomFichier.pdf";
documentWithFile
.id(8L)
.idCatalogue("idCatalogue")
.nom("nom")
.reference("reference")
.version("version")
.type(TypeDocument.IPID)
.fichier(new String(fileAsStringBase64))
.nomFichier(nomFichierExpected);
return documentWithFile;
}
}
If you look carrefully, the right way to mixup both : a mock and a spy of the same bean.
You can use #MockBean....and you use the spy version of this #MockBean with when(...).thenCallRealMethod(). But the REAL drawback of this is that if the #MockBean bean contains #Value field injection, thus they are NOT initialized. Meaning that #MockBean annotation set #Value fields of the mocked bean to null.
So I went for solution 2) because I need injection of #Value fileds.
Instead of #MockBean use #SpyBean of the concerned spring bean. Like this, you've got now the real bean. The question is how do I use it like a #MockBean. So to use a #SpyBean like a #MockBean, you needs to force the returned value of your #SpyBean bean like this for example :
doReturn(StringUtils.EMPTY).when(alfrescoServiceSpy).sendDocument(anyString(), any(InputStream.class), eq(documentWithFile.getNomFichier()));
As you can see, although alfrescoServiceSpy call the real code (not a mock), then you still can change its default behavior (calling the real code) with the mocked behavior like the snipped code above as example.
So test methods that need the mock version of #SpyBean declare an instruction of mocked behavior to do. And test methods that needs real code they don't do anything and #Value will be injected into the bean annotated #SpyBean the right way.
I'am personnaly satisfied of this code version.
Thanks you very much all.

Is it possible to retrieve value from application.properties without autowiring the class under test?

I have a test class that is annotated with #Spy and #InjectMocks and tested using Mockito. The class under test has a value (url) that is retrieved from the application.properties file. I'd like to test whether this url is being set correctly within the method that uses it. I can do this if I remove the #Spy and #InjectMocks annotations and use #Autowire and #SpringBootTest. However, that breaks other tests that use the spy functionality, so I'm just wondering if there's any way we can keep the spy working and test all our methods inside the same file, maybe without the need to bring in the #SpringBootTest annotation and the autowiring? The workaround we're using for now is to have two files, one that uses the spy and the other that tests the specific method to do with the properties file and that requires the full context to load, but not sure that's the best solution here?
Here is the test with the spy:
#ExtendWith(MockitoExtension.class)
class ProviderHelperServiceTest {
#Spy
#InjectMocks
ProviderHelperService providerHelperService;
#Value("${viaduct-url}")
String viaductUrl;
#Test
void testGetRequestBodyUriSpec() {
WebClient.RequestBodyUriSpec requestBodyUriSpec = providerHelperService.getRequestBodyUriSpec("sifToken");
final String[] url = new String[1];
requestBodyUriSpec.attributes(httpHeaders -> {
url[0] = (String) httpHeaders.get("org.springframework.web.reactive.function.client.WebClient.uriTemplate");
});
// Fails as url[0] comes back as null. Disabled and moved to another file.
assertEquals(viaductUrl, url[0]);
}
#SpringBootTest
class ProviderHelperService2Test {
#Autowired
ProviderHelperService providerHelperService;
#Value("${viaduct-url}")
String viaductUrl;
#Test
void testGetRequestBodyUriSpec() {
WebClient.RequestBodyUriSpec requestBodyUriSpec = providerHelperService.getRequestBodyUriSpec("sifToken");
final String[] url = new String[1];
requestBodyUriSpec.attributes(httpHeaders -> {
url[0] = (String) httpHeaders.get("org.springframework.web.reactive.function.client.WebClient.uriTemplate");
});
assertEquals(viaductUrl, url[0]);
}
}
And here is the method under test:
public class ProviderHelperService {
#Value("${viaduct-url}")
String viaductUrl;
public WebClient.RequestBodyUriSpec getRequestBodyUriSpec(String sifToken) {
WebClient.RequestBodyUriSpec requestBodyUriSpec = WebClient.create().post();
requestBodyUriSpec.header("Authorization", sifToken);
requestBodyUriSpec.uri(viaductUrl);
return requestBodyUriSpec;
}
}
The cleanest way to perform such tests is to replace field injection with constructor injection, and then you can quite easily confirm that the value that's passed into the class comes back out the service call.
If you're using Boot, it's usually best to replace use of #Value with #ConfigurationProperties. Depending on the specifics, you can either pass the whole properties object to the service's constructor or write an #Bean configuration method that unpacks the relevant properties and passes them as plain constructor parameters with new.

How to mock #Inject Api-Class with Mockito

I'm trying to test a Java method with Junit,
Unfortunately I can't get any further at one point because the Api class was defended as #Inject
I actually tried everything I could, unfortunately null is always returned and the test fails every time.
#Inject
private MemberAPi memberApi;
NewMember newMember = new NewMember();
newMember = MemberApi.addMember(new CreateMemberParameterObj (newMember, getId , false, Obj ))
Test: I try to mock it like that e.g.
#Mock
private MemberAPi mockedMemberApi;
when(mockedMemberAPi.addMember(anyObject())).thenReturn(anyObject());
Mock the MemberAPI and the NewMember classes.
Use #InjectMocks and Mockito will automatically inject the mockMemberAPI object.
Here is some code:
#InjectMocks
private Blam classToTest; // your class.
#Mock
private MemberAPi mockMemberAPi;
#Mock
private NewMember mockNewMember;
#Before
public void before()
{
MockitoAnnotations.openMocks(this);
doReturn(mockNewMember).when(mockMemberAPI).addMember(anyObject());
}
I use the doReturn().when().xxx();
pattern instead of the
when(mockedMemberAPi.addMember(anyObject())).thenReturn(mockMemberAPI);
pattern.
Note:
thenReturn(anyObject()); makes no sense because you can't return anyObject().

I am trying to Mock an autowired map of instances in spring but I get null pointer exception

In one of my controller
#Autowired
private Map<String, ABC> abcMap;
now I want mock it in one of the unit test but I always get null pointer exception.
This map contains implementations of ABC abstract class.
Can anyone suggest a possible solution?
I'm not sure what Unit test Framework you are using but there are ways of making it inject the mock details. You'll have to give us more information before before we can answer.
Personally I don't much like Autowired private fields, so at the risk of answering a different question can I suggest you consider using an Autowired constructor instead. From Springs POV it won't make a difference, your object will be create and all the appropriate data wired in. (OK, there is a slight change in the order things are done, but generally you won't notice). You will have to write a constructor to copy the constructor parameters to private fields, but:
Those fields could be made final, which could make your class safer
Your Unit tests wont need any 'magic' to initialise the Autowired fields - just pass parameters
If you refactor you class to remove add/remove/modify an Autowired field then you have to remember to change your test code. With an Autowired constructor you test code has to be changed or it won't compile, and your IDE might even help you do it.
Update
The Autowired constructor alternative looks something like:
#Controller
class MyClass {
private final Class1 bean1;
private final Object value2;
#Autowired
MyClass(Class1 bean1, Class2 bean2) {
this.bean1 = bean1;
this.value2 = bean2.getValue();
}
}
Keys points are:
The class has just one constructor and it requires parameters.
The fields are not annotated #Autowired, because Spring is not assigning values to them; the constructor does that.
The constructor IS annotated as #Autowired to tell Spring to pass the beans as parameters
The first parameter is stored in a final variable - you code can't accidentally over write it, so your code is safer
In my example the second parameter is only used in the constructor, so we don't have to store it as a field in your controller. I often to this if the Bean is an object that passes configuration around.
A No-argument constructor is not required
At test time your code will have to pass parameters to the class.
Your test code will look something like:
class MyClassTest {
private Class1 bean1;
private Class2 bean2;
private MyClass objectUnderTest;
#Before
public void setUp() throws Exception {
bean1 = mock(Class1.class);
bean2 = mock(Class2.class);
// Train mocks here
objectUnderTest = new MyClass(bean1, bean2)
}
#Test
public void myTest() {
// Do something with objectUnderTest
}
}
Key points are:
There are no #MockBean annotations
The Unit test is only using the API that your Controller bean defines; No black magic is required
It's not possible to create a MyClass with out providing the required data. This is enforced by the compiler
I think you can try it.
The sample of code:
public interface Animal {
}
#Service
public class Cat implements Animal{
}
#Service
public class Dog implements Animal{
}
#Service
public class Clinic {
#Autowired
private final Map<String, Animal> animalMap = new HashMap<>(2);
}
Sample of test
#Configuration
public class TestEnvConfig {
#Bean
public Clinic create(){
return new Clinic();
}
#MockBean // you can do it without mock or use #ComponentScan
Dog dog;
#MockBean
Cat cat;
}
#SpringBootTest(classes = TestEnvConfig.class)
#RunWith(SpringRunner.class)
public class ClinicTest {
#Autowired
private Clinic clinic;
}

My annotation #Value return null even it being used and called into component annotated classes

I'm using Spring and need some help:
I want to set one API key using application.properties instead of hardcoding it, but it always returns null. IntelliJ evaluates it correctly to the value I've set in the file.
I've already read other questions here and almost all solutions are saying that Spring can only "inject" those value anotations in managed classes, like Components, Beans, etc. That's what (think) I did and still got null!
Everything else is working as I intended. Any direction is appreciated!
My application.properties
api.someapiservice.key=08e...f
Class that uses the properties value:
#Component
public class ApiClient implements ApiClientInterface {
#Value("${api.someapiservice.key}")
private String API_KEY;
public ApiClient () {
System.out.println(API_KEY); //Returns null after spring log info: Initialized JPA EntityManagerFactory for persistence unit 'default'
...
}
Class that uses ApiClient:
#Component
public class SomeService {
private final SomeRepository someRepository;
private final ApiClient apiClient;
public PlaylistService(SomeRepository someRepository , ApiClient apiClient ) {
this.SomeRepository = SomeRepository;
this.apiClient = ApiClient;
}
Field injection can't possibly happen until after the instance is already constructed, so your #Value (or #Autowired) fields will always be null in the constructor. Move the #Value to a constructor parameter instead.
If you want to know what is the value of your #Value field on start up. You can use #PostConstruct annotation, or you can move #Value annotation on your class constructor.
private String API_KEY;
public ApiClient(#Value("${api.test.value}") String key) {
this.API_KEY= key;
System.out.println(this.API_KEY);
}
or using #PostConstruct Annotation
#Value("${api.someapiservice.key}")
private String API_KEY;
#PostConstruct
public void init() {
System.out.println(this.API_KEY);
}

Categories

Resources