spock, mock a method response in a spring bean - spring

I have an integration test written in groovy (spock) in spring boot application. One of the application beans is called Validator it has the follwoing method:
public void validateIssueDates(final List<Timestamp> issueDates) {
issueDates.forEach(issueDate -> {
final Timestamp now = Timestamp.valueOf(LocalDateTime.now());
if (issueDate.before(now)) {
throw new IllegalArgumentException("Issue date is before current date");
}
});
}
In the Validator class there are other methods. In my spock integration test I would like to mock response for that particular method only. In the following way:
Validator.validateIssueDates(_) >> null
I want other validations to take place, but not this one. Bascially I want to achieve this but with spock. I would like to eliminate the validateIssueDates() method from being executed

solution using Spock
It's done using [#SpringSpy][2].
First we annotate field with a spring bean we want to wrap in spy object. For example:
#SpringSpy
private CarValidator carValidator;
then in our test, in then part we define how we want to override method from a a bean/spy:
then:
3 * carValidator.validateIssueDates(_) >> null
Solution using Mockito (as an additional approach, it's not related to spock solution)
I have got that pretty easy using spy in Mockito. Despite many trials (and errors) with spock's spy, It just doesn't want to work. If I get that, I post it here. For now, I can only share Mockito solution:
#Profile("test")
#Configuration
public class BeanConfig {
#Bean
#Primary
public CarValidator getCarValidatorSpy(CarValidator validator) {
CarValidator carValidatorSpy = Mockito.spy(validator);
Mockito.doNothing().when(carValidatorSpy).validateIssueDates(Mockito.any(CarDto.class));
return carValidatorSpy;
}
}
That's all. Seems fairly straightforward.

Related

Is it a good practice to unit test controller classes in Spring MVC

I used to unit test my controller classes just like any other normal Java class. I mean without using Spring's MockMvc. But then I realized that this way I won't be able to be sure that I've set up the MVC configuration correctly. So if I have controller like this:
#Restcontroller
#RequestMapping("/cars")
public class CarController{
private CarService carService;
public CarController (CarService service){this.carService = service};
#GetMapping
public List<Car> getCar(#RequestParam("filter") String filter){
if(filter!=null && !filter.trim().equal("")){
//omitted for brevity
}
}
}
and if I unit test its getCar method directly, even if the test passes, that won't mean my controller is alright. So instead of unit testing, I started actually doing integration testing. Something like this:
mockMvc.perform(get("/v1/cars?filter = Honda")).... bla bla bla
Recently a question arose if we should first unit test and the integration test rest controllers. At first glance, it seems to me that integration test does, in the end, check the correct behavior of the controller. But on the other hand, how good is that to rely on integration test only.
I personally never found unit testing controllers useful. In an ideal case a Controller is relatively slim, as it only calls a few methods of service objects and returns a result. IMO unit testing would mean (over)using the verify() method. (I.e. did the controller call the service method with the correct arguments.)
For example in my case a well written controller method looks like this:
#LoggingInfo
#PostMapping(value = "/someRoute",
produces = "application/json",
consumes = "application/json")
#ApiOperation(value = "Some description", response = SomeDTO.class)
public #ResponseBody
CompletableFuture<ResponseEntity> someControllerMethod(
#Validated(ValidationSequence.class) #RequestBody SomeDTO someDTO) {
return service
.doSomething(someDTO)
.thenApply((String var) -> ResponseEntity.ok(ResponseDTO.builder()
.myField(Collections.singleton(var)).build()));
}
To what extent would unit testing this method add value to the application?
For me, the game-changer was the use of integration tests. Here it turns out if all the spring magic is working correctly, such as:
Validators that were triggered by annotations
The order of (different) validators
The converters (i.e. see Jackson in action)
Exception handlers (are the thrown exceptions actually caught by the annotated exception handlers)
Hope this helps.

Cant inject Spring dependencies to both Spock and Spring at same time

I'm having troubles with setting up my tests. I'm using latest version of SpringBoot and Spock Framework. First of all, I'm not configuring my beans "traditional" way. All my classes in package except Facade are package-scoped. I'm not using #Component, #Service, etc.
The only one class Im injecting is Repository. Let me show you my Configuration class
#Configuration
class SurveyConfiguration {
#Bean
SurveyFacade surveyFacade(SurveyRepository surveyRepository) {
ConversionUtils conversionUtils = new ConversionUtils();
SurveyValidator surveyValidator = new SurveyValidator();
SurveyCreator surveyCreator = new SurveyCreator(surveyRepository, conversionUtils, surveyValidator);
return new SurveyFacade(surveyCreator);
}
}
It works fine, I'v tested all scenarios manually (sending POST to certain endpoint). Let me show you example method from SurveyCreator class I want to test.
SurveyDTO createSurvey(final SurveyDTO surveyDTO) throws ValidationException, PersistenceException {
Survey survey = conversionUtils.surveyToEntity(surveyDTO);
surveyValidator.validate(survey);
Optional<Survey> savedInstance = Optional.ofNullable(surveyRepository.save(survey)); //Will throw NullPtr
return savedInstance.map(conversionUtils::surveyToDTO)
.orElseThrow(PersistenceException::new);
}
Like I said, during runtime it works fine. So lets move on to tests
#SpringBootTest
class SurveyFacadeTest extends Specification {
#Autowired
private SurveyRepository surveyRepository
private SurveyFacade surveyFacade = new SurveyConfiguration().surveyFacade(this.surveyRepository)
def "should inject beans"() {
expect:
surveyRepository != null
surveyFacade != null
}
def "should create survey and return id"() {
given:
Long id
when:
id = surveyFacade.createSurvey(SampleSurveys.validSurvey())
then:
id != surveyFacade
}
}
First test passes, so I understand I got everything ok for tests. But, I'm getting NullPointer in my java code in method I posted above. Looks like SurveyRepository isnt injected into java code during tests, because thats the one that causes this exception... Any ideas how to work around that, to have my Repository injected in both Spring application and Spock tests?
If there are no reasons against, I recommend you to run the test on the "underlying bean" (and not a manually created instance):
#Autowired
private SurveyFacade surveyFacade;

Mocking a constructor of #Autowired service(system under test)

I have to mock jerseyclient which is being created in Constructor of subjected service. Subjected service is System under test injected via Spring's #Autowired.
In constructor of the service client=client.create() method is written. We can't change this code(Although this is a code smell). I want to mock the jersey client but it is in constructor of the service. I am not able to mock this
sooo... long story short.. admitting you use mockito, in your src for test you should have an applicationcontext for your test.. usually we define one programmatically so, something along those lines..
import the .xml file you use for test purpose (in my case i imported the one for the mailserver, for the connection and for the authentication) instead of the one i use for the "local" environmnet. After then define a method to setup each and every of your service.
You might need to add a mock for your template resolver as well, but ultimately this all depends on your stack...
So based on your approach the final thing might be a bit different, but ultimately you're gonna do something along the lines of what i outline below:
#Configuration
#ImportResource(value = {
"classpath:applicationContext-jdbc-test.xml",
"classpath:applicationContext-ldap-test.xml",
"classpath:applicationContext-mail-test.xml"})
public class ApplicationTestContext {
#Bean
public ObjectMapperWrapper objectMapperWrapper() {
return Mockito.mock(ObjectMapperWrapper.class);
}
#Bean
public YourService yourService() {
return new YourServiceImpl();
}
}

writing junit for custom spring validator class

How to write a unit testing case for custom spring validator implementation class . For example
public class RegistrationValidator implements Validator.
Just wanted to know the various approaches . Am new to spring so exploring all options.
Thanks,
S
Create an Errors object,
Create an instance of your Validator,
invoke yourValidator(testData, errors)
Check that Errors is modified in the way you expected, in dependence to testData
#Test
public void testValidateWithUserWithoutLogin() {
User u = new User(); //your domain object, for example a user with "login=null"
Errors errors = new BeanPropertyBindingResult(u, "u");
MyValidator validator = newValidator();
validator.validate(p, errors); // 'validator' under test
assertTrue(errors.hasErrors());
assertNotNull( errors.getFieldError("login") );
}
BTW: you should have a look at JSR 303-Bean Validation, it is also supported by Spring 3.0. But it is easyer to use (less code) for the most use cases.

Unit testing with Mockito

I am writing unit tests for service layer in my spring application.
Here is my service class
#Service
public class StubRequestService implements RequestService {
#Autowired
private RequestDao requestDao;
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
#Override
public Request getRequest(Long RequestId) {
Request dataRequest = requestDao.find(requestId);
return dataRequest;
}
}
Here is my test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml" })
public class StubRequestServiceTest {
#Mock
public RequestDao requestDao;
StubRequestService stubRequestService; // How can we Autowire this ?
#org.junit.Before
public void init() {
stubRequestService = new StubRequestService(); // to avoid this
stubRequestService.setRequestDao(dataRequestDao);
// Is it necessary to explicitly set all autowired elements ?
// If I comment/remove above setter then I get nullPointerException
}
#Test
public void testGetRequest() {
Request request = new Request();
request.setPatientCnt("3");
when(requestDao.find(anyLong())).thenReturn(request);
assertEquals(stubRequestService.getRequest(1234L).getPatientCnt(),3);
}
}
Its working fine but I have few questions
How can we Autowire service class in test ? I am using constructor in init() method to create service object.
Do we have to set all Autowire element for service class ? For ex StubRequestService have autowired RequestDao which I need to set explicitly before calling test method otherwise it giveds nullPointerException as requestDao is null in StubRequestService.getRequest method.
Which are the good practices to follow while unit testing Spring service layer ? (If I am doing anything wrong).
Your test is fine. It doesn't even have to have the #ContextConfiguration annotation.
The whole point of dependency injection frameworks like Spring is to be able to unit test services by simply instantiating them, setting mock dependencies, and then call their methods.
You're doing it correctly. You don't need to have a Spring context for such unit tests. That's why they're called unit tests: they test it in isolation of all their actual dependencies, Spring included.
Side note: assuming you're using JUnit, the arguments of the assertXxx method should be swapped. The expected value comes before the actual value. It becomes important when the assertion fails and you have a message like "expecting 6 but was 3" rather than "expecting 3 but was 6".
If you really feel that it will make your tests easier to understand - you can initialize a spring context and fetch all of the objects from there. However, usually it will require creating a separate spring configuration XML file specifically for tests therefore I would not recommend it.
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
stubRequestService = (RequestService)applicationContext.getBean("myRequestServiceBean");
(and 3) Basically, I prefer testing each component of my application in total isolation from eachother and that's why I do not recommend what I described in [1].
What that means, is you take a separate logical slice of your application and test only it, while fully mocking up everything it tries to access.
Let's say you have three classes:
//Fetches stuff from some webservice and converts to your app domain POJOs
class DataAccessLayer {
public void setWebservice(Webservice ws) {...};
public MyObject getMyObject() {...};
}
//Formats the domain POJOs and sends them to some kind of outputstream or stuff.
class ViewLayer {
public void setOutputStream(OutputStream os) {...};
public void viewMyObject(MyObject mo) {...};
}
//Main entry point of our MyObject fetch-process-display workflow
class Controller {
public void setDataAccessLayer(DataAccessLayer dal) {...};
public void setViewLayer(ViewLayer vl) {...};
public void showMyObject() {
MyObject mo = dal.getMyObject();
...some processing here maybe...
vl.viewMyObject(mo);
}
}
Now, what tests can we write here?
Test if DataAccessLayer properly converts the object from mocked up WS to our domain object.
Test if ViewLayer properly formats the object given to him and writes it to mocked up output stream.
Test if Controller takes an object from mocked up DataAccessLayer processes it properly and sends it to mocked up ViewLayer.
Or You can use springockito
https://bitbucket.org/kubek2k/springockito/wiki/Home, it will make your tests cleaner

Resources