How to use session in unit test? - spring

I have a spring service method that gets an object stored in the session (using FacesContext) as follows:
(MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
and I would like to put that object in session in unit test before invoking the method.
so i tried the solution in this post:
Spring Test session scope bean using Junit
and in my test method i put the object in session before calling the service, but the service throws an exception when trying to get the object from the session, i guess that this is because the facescontext is not available, what do you think ?
I am using Spring, Junit, JSF2, please advise, thanks.

With Spring 3.2 this is very easier
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(...)
#WebAppConfiguration
public class SessionTest {
#Autowired
MockHttpSession session;
#Test
public void sessionAttributeTest() throws Exception {
MyObject myObject = session.getAttribute("myObject");
...
}
}
More information: Request and Session Scoped Beans

I'm assuming that you're talking about HttpSession.
Create a mock session, tell the mock session to always return this object when its getAttribute method is called with the name used by the object under test, and pass this mock session rather than a real one to the object under test.
Mocking APIs such as Mockito or EasyMock will help doing that.
EDIT: Suppose the method you want to test looks like this:
public String foo() {
// some lines of code
MyObject o =
(MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
// some more lines of code, using o.
}
You could refactor it like this:
public String foo() {
// some lines of code
MyObject o = getMyObjectFromSession();
// some more lines of code, using o.
}
protected MyObject getMyObjectFromSession() {
return (MyObject)((HttpServletRequest) FacesContext
.getCurrentInstance().getExternalContext().getRequest())
.getSession().getAttribute("myObject");
}
And you could then use a mocking framework to do something like this (pseudo-code):
// mockFoobar is the object to test. We just mock its getMyObjectFromSession method
FooBar mockFoobar = mock(Foobar.class);
MyObject objectInSession = new MyObject();
when(mockFoobar.getMyObjectFromSession()).thenReturn(objectInSession);
String s = mockFoobar.foo();
assertEquals("expected result", s);

Add spring-mock to your test classpath which gives you org.springframework.mock.web.MockHttpSession. This is a pretty simple implementation that you can create with new without a Java EE container.
The JAR also contains mocks for requests, responses and everything else.
Another solution is to use MockRunner which does the same.

Related

Mock a MDC data in spring boot test

I want to mock a data which is fetched from MDC in test class other wise when the code executed it return a null value. So i try below,
#RunWith(SpringRunner.class)
#SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.MOCK,classes = TestApp.class)
public class Test {
private MockMvc restMvc;
#Before
public void setUp() {
mock(MDC.class);
this.restMvc = MockMvcBuilders.standaloneSetup(TestController).build();
}
#Test
public void testMe() throws Exception {
when(MDC.get("correlation-id")).thenReturn("1234");
//Req param and header are intialized
restMvc.perform(get("/assignments").headers(headers).params(reqParams).principal(token).accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().is2xxSuccessful());
}
}
But i am getting error,
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
To mock a static method you should use Mockito.mockStatic method like this:
try (var mockStatic = Mockito.mockStatic(MDC.class)) {
mockStatic.when(() -> MDC.get("correlation-id"))
.thenReturn("1234");
// rest of the test...
}
You can read more about it in the Mockito documentation. Additionally, I've reproduced the problem and tested the solution - you can see a commit in a GitHub repository with all required code. Please note that usage of mockito-inline is required for this to work - see this part of the docs.
In case of your test, it would probably better to go with the approach proposed by #knittl in the comment - instead of mocking the get method, a value can be set in the MDC using the put method: MDC.put("correlation-id", "1234"). I've included both approaches in the GitHub repo mentioned before - both tests pass.

spock, mock a method response in a spring bean

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.

How to mock private method in public method in Spring Boot with JUnit

I'd like you ask a few questions and ask you for advice:
I want to test my public method (I use Spring Boot, Mockito, JUnit):
#Service
public class MyClass{
public Long getClientId(List<String> nameSurname) throws AuthorizationException {
Long operatorId;
if(...){
(... something not interesting ...)
User user = getUserByLogin("AnthonyGates2");
operatorId = nonNull(user) ? user.getOperatorId() : null;
} else {
List<User> users = getUserListByLogin("AnthinyGates");
operatorId = isNotEmpty(users) ? return operatorId;
return operatorId;
}
How to test the method getClientId?
Methods getUserByLogin and getUserListByLogin are private in this class (MyClass) but I have to mock the results of these private methods because these methods retrieve data from an external service.
These private methods looks like:
User user = DelegateImpl.getDelegate().getUserByLogin(nameAndSurname);
DelegateImpl.getDelegate().getUserByLogin get data from database and that data have to be mocked like:
when(DelegateImpl.getDelegate().getUserByLogin(any())).thenReturn(user);
How can I test my public class? Should I use PowerMock/PowerMockito? Making these methods public is in my opinion ugly because these methods are called only in MyClass. I can't find a good tutorial in Internet for my case (Spring Boot, Mockito, JUnit).
Thank you very much for all your tips!
Best regards
Matthew
Test the unit only by calling the public methods. I think that your example is a class in the service layer (contains business logic) and the two getUser... methods should be in a different class (I think in the data layer) where they can be public. Inject that class via the constructor as a dependency (in the service object) so you can mock it when testing the service class. The data layer class (with the getUser... methods) can also be tested by it's own unit tests.
If you are not able to unit test a method/class then it most probably means that it just does too much. Try extracting your private methods to a separate class. It does not need to be public - you can e.g. have it package-local in the same package.
Later, in the test, you would have to inject a mock of this class and simulate its behaviour.
The setup of MyClass in its unit test could look similar to this:
AnotherClass anotherClassMock = Mockito.mock(AnotherClass.class);
MyClass myClass = new MyClass(anotherClassMock);
Where AnotherClass would have methods getUserListByLogin and getUserByLogin.
EDIT:
It seems that the logic within in your private methods already call an external class. The problem is that you obtain an instance of an object via a call to a static getDelegate() method in another class.
Here's what you can do:
Create a new field in MyClass which would have the same type as the one returned by getDelegate() method (I don't know what that is, I'll call it Delegate)
Have 2 constructors: a default one which would assign the result of getDelegate method to your new field and another one which would take an instance of Delegate as a parameter and assign it to your field
In tests use the second constructor to create an instance of MyClass and pass a mock of Delegate class
It would look more ore less like this:
class MyClass() {
private Delegate delegate;
MyClass() {
this.delegate = DelegateImpl.getDelegate();
}
MyClass(Delegate delegate) {
this.delegate = delegate;
}
// ... the rest
}

Spring nested transactions

In my Spring Boot project I have implemented following service method:
#Transactional
public boolean validateBoard(Board board) {
boolean result = false;
if (inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
update(board, new Date(), Board.AFK);
throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
}
if (!canSelectCards(board)) {
update(board, new Date(), Board.COMPLETED);
throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
}
result = true;
}
return result;
}
Inside this method I use another service method which is called update:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
board.setStatus(status);
board.setFinishedDate(finishedDate);
return boardRepository.save(board);
}
I need to commit changes to database in update method independently of the owner transaction which is started in validateBoard method. Right now any changes is rolling back in case of any exception.
Even with #Transactional(propagation = Propagation.REQUIRES_NEW) it doesn't work.
How to correctly do this with Spring and allow nested transactions ?
This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
However, there is an option to switch to AspectJ mode
Using "self" inject pattern you can resolve this issue.
sample code like below:
#Service #Transactional
public class YourService {
//... your member
#Autowired
private YourService self; //inject proxy as an instance member variable ;
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void methodFoo() {
//...
}
public void methodBar() {
//call self.methodFoo() rather than this.methodFoo()
self.methodFoo();
}
}
The point is using "self" rather than "this".
The basic thumb rule in terms of nested Transactions is that they are completely dependent on the underlying database, i.e. support for Nested Transactions and their handling is database dependent and varies with it.
In some databases, changes made by the nested transaction are not seen by the 'host' transaction until the nested transaction is committed. This can be achieved using Transaction isolation in #Transactional (isolation = "")
You need to identify the place in your code from where an exception is thrown, i.e. from the parent method: "validateBoard" or from the child method: "update".
Your code snippet shows that you are explicitly throwing the exceptions.
YOU MUST KNOW::
In its default configuration, Spring Framework’s transaction
infrastructure code only marks a transaction for rollback in the case
of runtime, unchecked exceptions; that is when the thrown exception is
an instance or subclass of RuntimeException.
But #Transactional never rolls back a transaction for any checked exception.
Thus, Spring allows you to define
Exception for which transaction should be rollbacked
Exception for which transaction shouldn't be rollbacked
Try annotating your child method: update with #Transactional(no-rollback-for="ExceptionName") or your parent method.
Your transaction annotation in update method will not be regarded by Spring transaction infrastructure if called from some method of same class. For more understanding on how Spring transaction infrastructure works please refer to this.
Your problem is a method's call from another method inside the same proxy.It's self-invocation.
In your case, you can easily fix it without moving a method inside another service (why do you need to create another service just for moving some method from one service to another just for avoid self-invocation?), just to call the second method not directly from current class, but from spring container. In this case you call proxy second method with transaction not with self-invocatio.
This principle is useful for any proxy-object when you need self-invocation, not only a transactional proxy.
#Service
class SomeService ..... {
-->> #Autorired
-->> private ApplicationContext context;
-->> //or with implementing ApplicationContextAware
#Transactional(any propagation , it's not important in this case)
public boolean methodOne(SomeObject object) {
.......
-->> here you get a proxy from context and call a method from this proxy
-->>context.getBean(SomeService.class).
methodTwo(object);
......
}
#Transactional(any propagation , it's not important in this case)public boolean
methodTwo(SomeObject object) {
.......
}
}
when you do call context.getBean(SomeService.class).methodTwo(object); container returns proxy object and on this proxy you can call methodTwo(...) with transaction.
You could create a new service (CustomTransactionalService) that will run your code in a new transaction :
#Service
public class CustomTransactionalService {
#Transactional(propagation= Propagation.REQUIRES_NEW)
public <U> U runInNewTransaction(final Supplier<U> supplier) {
return supplier.get();
}
#Transactional(propagation= Propagation.REQUIRES_NEW)
public void runInNewTransaction(final Runnable runnable) {
runnable.run();
}
}
And then :
#Service
public class YourService {
#Autowired
private CustomTransactionalService customTransactionalService;
#Transactional
public boolean validateBoard(Board board) {
// ...
}
public Board update(Board board, Date finishedDate, Integer status) {
this.customTransactionalService.runInNewTransaction(() -> {
// ...
});
}
}

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