Test needing a mock of RabbitTemplate - java

I have a implementation class which acts as a rabbitMQ sender class, I am trying to write unit test cases for this, but i am having doubts about mocking rabbitmq template.
This is my sender class code:
#Service
public class Car implements CarDelegate {
#Autowired
private RabbitTemplate rt;
#Value("${exchange}")
private String exchange;
#Value("${queue}")
private String queue;
#Override
public ResponseEntity<String> createCar(String model, String name) {
Car car = new Car();
car.setModel(Model);
car.setName(Name);
String jsonString;
jsonString = new ObjectMapper().writeValueAsString(car);
try {
rt.convertAndSend(exchange, queue, jsonString);
} catch (AmqpException e) {
//to implement
}
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}
}
My sender class is also my implementation method.
The test class for it is as below:
#RunWith(MockitoJUnitRunner.class)
public class CarTest {
private Car car;
#Mock
private RabbitTemplate rt;
#Test
public void create_valid() {
Car car = new Car(rt);
car.create("sedan", "arison");
String jsonString = "";
Mockito.doReturn("")
.when(rabbitTemplate.convertAndSend(null, null, jsonString))
.myMethod(Mockito.any(createLeadTest_valid.class));
Mockito.when(rabbitTemplate.convertAndSend(null, null, jsonString)).thenReturn("");
}
}
What is the correct way to mock rabbit template

For your specific case, no need to add behavior to your mock.
public class CarServiceTest {
#Test
public void create_valid() {
RabbitTemplate rt = Mockito.mock(RabbitTemplate.class);
CarService car = new CarService(rt);
ResponseEntity<String> response = car.create("sedan", "arison");
assertThat(response).isNotNull();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
}
FYI, it is not good practice to manipulate ResponseEntity outside an HTTP adapter (typically a bean annotated with #Controller).
And RabbitTemplate#convertAndSend is supposed to provide a conversion mechanism, so you do not have to use Jackson directly.
Hoping this will help you !

Related

Not able to read values using #Value in application.yaml in Springboot

I'm using Spring boot - 2.3.3.RELEASE. There are some values in application.yaml which I'm trying to inject in the classes using #Value annotation. But for some reason, they are not loading up. The result should be that, in SendPhoneByPhoneNumbers.java, we should be able to read notificationServiceURL from application.yaml.
Note- I'm using Factory and Strategy pattern. This project is going to be used as a Library for other projects to import and use the methods exposed by Service layer.
Here is the folder structure: https://imgur.com/a/jYr7wyP
I'm trying to test by running Demo.java in Debug mode to see how the actual values looks like.
application.yaml
notificationService:
url: "https://someURL.com"
Demo.java
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#SpringBootApplication
public class Demo {
public static void main(String[] args) {
SpringApplication.run(Demo.class, args);
String title="Title";
String message="message";
List<String> phoneNumbers = new ArrayList<>();
phoneNumbers.add("333-222-1111");
PhoneService phoneService = new PhoneService();
phoneService.sendNotificationByPhoneNumbers(title, message, phoneNumbers);
}
}
PhoneService.java
#Service
public class PhoneService {
PhoneServiceImpl notificationServiceImpl = new PhoneServiceImpl();
public void sendNotificationByPhoneNumbers(String title, String message, List<String> phoneNumbers) {
notificationServiceImpl.sendNotificationByPhoneNumbers(title, message, phoneNumbers);
}
}
PhoneServiceImpl.java
#Slf4j
#Component
public class PhoneServiceImpl {
#Value("${notificationService.url}")
String url;
public void sendNotificationByPhoneNumbers(String title, String message, List<String> phoneNumbers) {
PhoneContext phoneContext = new PhoneContext(new SendPhoneByPhoneNumbers(url));
phoneContext.notify(title, message, phoneNumbers);
}
}
PhoneContext.java
public class PhoneContext {
private PhoneStrategy phoneStrategy;
public PhoneContext(PhoneStrategy phoneStrategy){
this.phoneStrategy = phoneStrategy;
}
public void notify(String title, String message, List<String> employees){
phoneStrategy.sendNotification(title, message, employees);
}
}
PhoneStrategy.java
public interface PhoneStrategy {
public void sendNotification(String title, String message, List<String> listOfEmployeeIdGroupNamePhoneNumbers);
}
SendPhoneByPhoneNumbers.java
#Slf4j
public class SendPhoneByPhoneNumbers implements PhoneStrategy {
RestTemplate restTemplate;
String notificationServiceURL;
BuildHttpRequest buildHttpRequest;
public SendPhoneByPhoneNumbers(String notificationServiceURL) {
this.notificationServiceURL = notificationServiceURL;
this.restTemplate = new RestTemplate();
this.buildHttpRequest = new BuildHttpRequest();
}
#Async
public void sendNotification(String title, String message, List<String> phoneNumbers) {
SmsMessage smsMessage= new SmsMessage(title, message, phoneNumbers, Collections.emptyList(), Collections.emptyList());
try {
HttpHeaders headers = new HttpHeaders();
headers.set("idToken", buildHttpRequest.getNewToken());
HttpEntity<SmsMessage> newRequest = new HttpEntity<>(smsMessage, headers);
restTemplate.postForObject(notificationServiceURL + "/someUrl", newRequest, String.class);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
Also, if you guys have any suggestions on modifying the code/structure in an way to make it better, please do suggest.
Thanks in advance.
PhoneService phoneService = new PhoneService();
Since PhoneService that you are using is not a managed bean by spring container, the values are not being injected.
Code Improvement and FIX
String title="Title";
String message="message";
List<String> phoneNumbers = new ArrayList<>();
phoneNumbers.add("333-222-1111");
// PhoneService phoneService = new PhoneService();
phoneService.sendNotificationByPhoneNumbers(title, message, phoneNumbers);
Move this code to a class implementing CommanLineRunner or ApplicationRunner and override corresponding run(). In this class, you could #Autowire PhoneService rather than manually instantiating it. Also note that you have to mark this class with #Component
Other minor suggested changes:
You can make SendPhoneByPhoneNumbers class a singleton. If there are multiple implementions, use #Qualifier
Both RestTemplate and BuildHttpRequest could be created using #Bean annotation.
Since you are using lombok, using #RequiredArgsConstructor could also be considered.

Spring Unit Test Rest Controller By Setting Private Fields

I have a simple Rest Controller as below
#RestController
public class HealthController {
private static final CustomLogger logger = CustomLogger.getLogger(HealthController.class.getName());
private HealthService healthService;
#Autowired
public HealthController(HealthService healthService) {
this.healthService = healthService;
}
#RequestMapping(value = "/health", method = RequestMethod.GET)
public ResponseEntity<?> healthCheck() {
return healthService.checkHealth();
}
}
The service class is below
#Service
public class HealthService {
private static final CustomLogger logger = CustomLogger.getLogger(HealthController.class.getName());
public ResponseEntity<?> checkHealth() {
logger.info("Inside Health");
if (validateHealth()) {
return new ResponseEntity<>("Healthy", HttpStatus.OK);
} else {
return new ResponseEntity<>("Un Healthy", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
boolean validateHealth() {
return true;
}
}
The corresponding unit test for the controller class as below
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = HealthController.class)
public class HealthControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HealthService healthService;
#Test
public void checkHealthReturn200WhenHealthy() throws Exception {
ResponseEntity mockSuccessResponse = new ResponseEntity("Healthy", HttpStatus.OK);
when(healthService.checkHealth()).thenReturn(mockSuccessResponse);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/health").accept(
MediaType.APPLICATION_JSON);
MvcResult healthCheckResult = mockMvc
.perform(requestBuilder).andReturn();
Assert.assertEquals(HttpStatus.OK.value(), healthCheckResult.getResponse().getStatus());
}
}
The problem I have is my CustomLogger. Since it has external dependencies am having issues in trying to test this.The same kind of logger is present in my service classes too.
How can I test such a class. I tried the below stuffs
Created a custom class name CustomLoggerForTest under test. Used
ReflectionTestUtils.setField(healthService, "logger", new CustomerLoggerForTest(HealthService.class.getName()));
in the setUp. But it did not help. Using this we cannot set the static fields hence tried even converting them to be non-static
Tried with mocking the CustomLogger in setup as below
mockStatic(CustomLogger.class); when(CustomLogger.getLogger(any())) .thenReturn(new CustomLoggerForTest(HealthController.class.getName()));
But no luck.
Is there anything that am doing wrong that is causing this?

Spring autowired Variable is not available for usage

Am new to spring and spring boot. I was actually trying to a restclient in springboot. When i write the client and get the response, i wanted to read the response body which is String and wanted to convert that to JSON for my use. So i have written RestClient class and from that I have autowired JsonUtil class which does String to JSON. But my autowired jsonutil is not available for me to use in Rest client class. i dont know what i need to do here. Below is my code.
My RestClient
#Component
public class RestClient {
#Autowired
JsonUtil jsonUtil;
private static final String URL ="https://test.com?q=";
private static String getURL(String value){
if(!StringUtils.isBlank(value))
return URL+value;
return null;
}
private static void get(String val){
RestTemplate restTemplate = new RestTemplate();
String resourceUrl=getURL(val);
ResponseEntity<String> response = null;
if(!StringUtils.isBlank(resourceUrl)){
response = restTemplate.getForEntity(resourceUrl , String.class);
}
//Though i have autowired JsonUtil, i dont have that object to use it here
jsonUtil. //this variable is not available
}
My JsonUtil
import com.fasterxml.jackson.databind.ObjectMapper;
#Component
public class JsonUtil {
#Autowired
private ObjectMapper objectMapper;
public JsonUtil(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public JsonNode getStringAsJson(String value) {
try {
return objectMapper.readTree(value);
}catch (IOException e) {
String msg = e.getMessage();
LOG.info(msg);
}
return null;
}
}
Any help appreciated
You are trying to use an instance variable inside a static function. That is not possible. Think of it this way, there can be numerous instances of the class with each having its own particular instance variable. How will the static method know which one to pick.
You should make the methods non static to make it work.
However, if you are determined to make the function static make the variable also static and since you can't autowire static fields, try something like this
private static JsonUtil jsonUtil;
#Autowired
ApplicationContext ctx;
#PostConstruct
public void init() {
jsonUtil = ctx.getBean(JsonUtil.class);
}

Java testing with mockito: mock class object

I would like to mock an object inside my class.
public class Controller{
private StandardEmailSender sender = new StandardEmailSender();
public void process() throws EmailException {
// do some stuff
sender.sendEmail(to, subject, body);
// do some stuff
}
}
I would like to mock sender.sendEmail(to, subject, body);. I've spent time finding a solution but I'm stuck. I tried to mock directly the object StandardEmailSender like this :
#Mock
StandardEmailSender sender;
#Before
public void setUp() throws EmailException {
MockitoAnnotations.initMocks(this);
doNothing().when(sender).sendEmail(anyString(), anyString(), anyString());
}
#Test
public void test() throws EmailException {
Controller controller= new Controller ();
controller.process();
//make some asserts
}
Would someone have a solution to my problem? Thanks!
You have two choices here:
make it possible for your test case to "manually" inject a (mocked) Sender object (for example by providing a constructor to set that field)
make use of Mockitos #InjectMocks annotation
A typical approach for option 1 is to use constructor telescoping, like this:
public class Controller {
private final Sender sender;
public Controller() { this(new StandardEmailSender()); }
Controller(Sender sender) { this.sender = sender; }
By doing so, clients can still create a Controller instance without worrying about providing a sender, and your unit tests can use that package protected constructor to provide a mocked sender instance.
Use a form of dependency injection, for example:
public class Controller{
private final EmailSender sender;
Controller(EmailSender emailSender) {
this.sender = Objects.requireNonNull(emailSender);
}
public Controller() {
this(new StandardEmailSender());
}
}
In your test:
#Test
public void test() throws EmailException {
Controller controller= new Controller(mockedSender);
controller.process();
}

What is the best way to mock object instantiated inside method under test?

I have the following code and I look for the best way to test it:
public class ClientFactory {
private ConfigurationLoader loader;
public ClientFactory(ConfigurationLoader loader) {
this.loader = loader;
}
public IRest create(String something) {
Configuration config = loader.load(something);
if (magic()) {
return new ClientType1(config);
}
return new ClientType2(config);
}
}
public class ClientType1 implements IRest{
private Configuration config;
public ClientType1(Configuration config) {
this.config = config;
}
public Something doGetRequest(Long id) {
WebClient client = getHttpClient();
return client.get();
}
private WebClient getHttpClient() {
WebClient client = new WebClient();
client.setSchema(config.getSchema());
client.setHostname(config.getHostname());
client.setPort(config.getPort());
// and so on ....
return client;
}
}
I would like to test the interaction/behaviour between ConfigurationLoader and ClientType1.getHttpClient methods. From one side I think it is good idea, testing interaction between objects, from the other side, hmmm I test setters and getters - boring, no business logig is involved here. Which one is more true?
Mock of configuration object can be easily transferred into ClientType1 when it is instantiated, mocking the 'new WebClient()' seems to be the problem. I thought about:
public class ClientType1 implements IRest{
private Configuration config;
private WebClient client; // this will be replaced by mock
public ClientType1(Configuration config) {
this.config = config;
webClient = new WebClient();
}
.....
private Client getHttpClient() {
client.setSchema(config.getSchema());
....
return client;
}
}
and use PowerMock to replace private WebClient client by mock, but I am not sure it is java way. Any guidelines/suggestions?
As you have found, the new keyword makes unit testing difficult. I suggest avoiding it. I think your problem here is more of a design problem. Objects should not configure themselves. When you design an object, think about what its true dependencies are. IMO the true dependency of the ClientType1 is a WebClient or a pool of WebClient not a Configuration. IMO the true dependency of ClientFactory is a Configuration not a String.
I would redesign like so:
interface ClientFactory {
IRest create(Configuration config);
}
public class DefaultClientFactory implements ClientFactory {
private final ClientFactory magicClientFactory;
private final ClientFactory otherClientFactory;
public DefaultClientFactory(ClientFactory magicClientFactory, ClientFactory otherClientFactory) {
this.magicClientFactory = magicClientFactory;
this.otherClientFactory = otherClientFactory;
}
public IRest create(Configuration config) {
if (magic()) {
return magicClientFactory.create(config);
} else {
return otherClientFactory.create(config);
}
}
}
interface WebClientFactory {
WebClient create(Configuration config);
}
public class DefaultWebClientFactory implements WebClientFactory {
public WebClient create(Configuration config) {
WebClient client = new WebClient();
client.setSchema(config.getSchema());
client.setHostname(config.getHostname());
client.setPort(config.getPort());
return client;
}
}
public class ClientType1Factory implements ClientFactory {
private final WebClientFactory webClientFactory;
public ClientType1Factory(WebClientFactory webClientFactory) {
this.webClientFactory = webClientFactory;
}
public IRest create(Configuration config) {
return new ClientType1(webClientFactory.create(config));
}
}
public class ClientType1 implements IRest{
private final WebClient webClient;
public ClientType1(WebClient webClient) {
this.webClient = webClient;
}
public Something doGetRequest(Long id) {
return webClient.get();
}
}
Using this design you can successfully unit test each and every class defined without resorting to advanced features of PowerMock. You can unit test ClientType1 by passing in a mock WebClient. You can also test your factories by passing in different configurations and checking if the created object is what you expect. Further the code is less coupled, more flexible, and each class has a single responsibility.

Categories

Resources