How to mock findByPrincipalName in spring\mockito - spring

I am trying to mock findByPrincipalName as in my test context I do not have redis set up but I am unable to do so, I get the following error:
The method thenReturn(Map<String,capture#2-of ?>) in the type OngoingStubbing<Map<String,capture#2-of ?>> is not applicable for the arguments (Map<String,capture#3-of ? extends Session>)
I do not really understand what this error is telling me, below is how I am attempting to mock the method:
Map<String, ? extends Session> sessions = new HashMap<>();
#MockBean
private FindByIndexNameSessionRepository<?> sessionRepository;
when(this.sessionRepository.findByPrincipalName(VALID_SUB)).thenReturn(sessions);
What do I need to do to be able to mock this method? The class RedisSession is not accessible so I cannot create an instance of this to use.

This is not a problem related to mocking, but simply a generic type mismatch. You defined the repository as FindByIndexNameSessionRepository<?>, while your sessions reference type is Map<String, ? extends Session>, so your repository returns ? (2), while you're trying to return an object containing ? extends Session (3). The numbering in the last sentence marks the bounds (?) accordingly to the log you've provided - bounds defined in different places are treated as different type definitions and do not match (read more here).
What you need to do is: define types for both the repository and the object it should return so that they match. One way of doing that would be simply sticking to the interface (Session) or if you wanted to make it more concrete, you could use a generic type definition on the class level (<T extends Session>) and apply it to the repository and the map.
#MockBean
private FindByIndexNameSessionRepository<Session> sessionRepository;
#Test
void test() {
Map<String, Session> sessions = new HashMap<>();
when(sessionRepository.findByPrincipalName(VALID_SUB))
.thenReturn(sessions);
...
}
class TypedIndexNameSessionTest<T extends Session> {
#MockBean
private FindByIndexNameSessionRepository<T> sessionRepository;
#Test
void emptySessions() {
Map<String, T> sessions = new HashMap<>();
when(sessionRepository.findByPrincipalName(VALID_SUB))
.thenReturn(sessions);
...
}
}
I've tested the code locally and pushed it to my GitHub repository - you can see the full example there (all tests pass).

Related

Restore Spring State Machine from Database

I have been following the data persist examples outlined in the spring docs
When I acquire a state machine it doesn't pull it from the database but checks in memory. I see records being written but I just can't restore them. Here is my configuration
I have defined the StateRepository and the
private final StateRepository<? extends RepositoryState> stateRepository;
private final TransitionRepository<? extends RepositoryTransition> transitionRepository;
private final StateMachineRuntimePersister<LoanEventStatus, LoanEventAction, String> stateMachineRuntimePersister;
Here are my beans based on the examples
#Bean
public StateMachineModelFactory<String, String> modelFactory() {
return new RepositoryStateMachineModelFactory(stateRepository, transitionRepository);
}
#Bean
public DefaultStateMachineService<LoanEventStatus, LoanEventAction> stateMachineService(
final StateMachineFactory<LoanEventStatus, LoanEventAction> stateMachineFactory,
final StateMachinePersist<LoanEventStatus, LoanEventAction, String> stateMachinePersist) {
return new DefaultStateMachineService<LoanEventStatus, LoanEventAction>(stateMachineFactory, stateMachinePersist);
}
#Bean
public StateMachinePersister<LoanEventStatus, LoanEventAction, String> persister(
StateMachinePersist<LoanEventStatus, LoanEventAction, String> defaultPersist) {
return new DefaultStateMachinePersister<>(defaultPersist);
}
I thought restoring them would be as simple as the following:
private final DefaultStateMachineService<LoanEventStatus, LoanEventAction> stateMachineService;
var stateMachine = stateMachineService.acquireStateMachine(id);
However, when I step into the DefaultStateMachineService I notice that the machines are empty. Shouldn't it restore when the application starts up or am i missing something
So I realized the problem was with this method.
stateMachineService.hasStateMachine(id)
Looking at the code it only looks for state machines that are in memory. To get the ones that are in the database you need to use JpaRepositoryStateMachine
StateMachineRepository<JpaRepositoryStateMachine> stateMachineRepository;
Then do the following to test if there is a state machine in the database and create one if there is not.
Optional<JpaRepositoryStateMachine> instance = stateMachineRepository.findById(Integer.toString(loanDetailId));
if (instance.isPresent()){
StateMachine<State, Event> stateMachine = stateMachineService.acquireStateMachine(id);

Capturing entity information in custom entity listener

I would like a custom entity listener to generate an auto-incremented alias for a few of the entities.
I have implemented one util class in order to generate auto incremented alias for the entities in a distributed environment as follows:
#Component
public class AutoIncrementingIdGenerationUtil {
private final RedisTemplate<String, Object> redisTemplate;
public AutoIncrementingIdGenerationUtil(
RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String getNextSequenceNumber(String keyName) {
RedisAtomicLong counter = new RedisAtomicLong(keyName,
Objects.requireNonNull(redisTemplate.getConnectionFactory()));
return counter.incrementAndGet();
}
}
Now, I have several entities in my application, for a FEW OF ENTITIES, I would like to generate the alias.
So I am writing my own custom entity listener as follows:
#Component
public class CustomEntityListener<T> {
private final AutoIncrementingIdGenerationUtil autoIncrementingIdGenerationUtil;
public CustomEntityListener(
AutoIncrementingIdGenerationUtil autoIncrementingIdGenerationUtil) {
this.autoIncrementingIdGenerationUtil = autoIncrementingIdGenerationUtil;
}
#PrePersist
void onPrePersist(Object entity) { <----HERE I WOULD LIKE TO CAST TO CONCRETE ENTITY TYPE,
if(StringUtils.isBlank(entity.getAlias())) {
entity.setAlias(autoIncrementingIdgenerationUtil.getNextSequenceNumber(entity.getEntityType());
}
}
As mentioned above, all of the entities do not have an alias attribute. I am not getting any proper idea regarding how to do this. One bad idea is to use getTEntityype(). But in this case, it would be too many if-else and typecast accordingly, which will not look good. Any better idea regarding how to do it?
Another related question in the same context, if I have an entity having a #PrePersist function already, will the function defined in entity listener override this, OR will both of them run?
Entity listeners cannot be parameterized. Just make the relevant entities implement an interface, e.g. Aliased, with a setAlias() method. You'll then have a single type to cast to.
Also, why use Redis? Doesn't your DB have sequences?

How to use #Autowired in an class annotated with #Entity?

I have an entity called TimeBooking. When I request this entity and return to the client I want to get a list of ActivityTimeBookings from a repository. But when the function get called the repo is null.
So I tried to #Autowired the repo and marked it as transient and also said Spring that there is a dependency which should be injected.
#Configurable(preConstruction = true)
#Entity
public class TimeBooking extends BaseEntity{
#Autowired
private transient ActivityTimeBookingRepository activityTimeBookingRepository;
...
#JsonProperty("activityTimeBookings")
private List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Any suggestions?
Using #Autowired in a class annotated with #Entity is a bad practice.
The solution is given below :
1. Create a service interface :
public interface TimeBookingService {
public List<ActivityTimeBooking> activityTimeBookings();
}
2. Create an implementation of the service interface :
#Service
public class TimeBookingServiceImpl implements TimeBookingService {
#Autowired
private ActivityTimeBookingRepository activityTimeBookingRepository;
public List<ActivityTimeBooking> activityTimeBookings() {
return this.activityTimeBookingRepository.findByDate(this.timeFrom);
}
}
Usually its indeed a bad practice to inject something into JPA entities.
These are usually created by JPA implementation (like Hibernate) and spring as a DI framework doesn't really participate in this process.
Note, that there can be many instances of this class created as a result of query, so if you later use this for serialization of the list of this object you might end up running N queries to the database given N entities like this were retrieved.
Answering your question about "getting access to the repo" I believe you should consider refactoring:
In the service class (assuming you have a "regular" contoller, service and dao):
you can:
class MyService {
SomeResult.. doSomething() {
List<TimeBooking> allTimeBookings = dao.getAllTimeBooking();
LocalDateTime timeFrom = calculateTimeFrom(allTimeBookings);
List<ActivityTimeBooking> allActivityTimeBookings = dao.findByDate(timeFrom);
return calculateResults(allTimeBookings, allActivityTimeBooking);
}
}
class MyDao {
List<ActivityTimeBooking> findByDate(LocalDateTime timeFrom) {...}
List<TimeBooking> getAllTimeBookings() {...}
}
Regarding the service implementation, I've assumed this use case can't be covered by usual "JOIN between two tables" so that that creating an association between TimeBooking and ActivityTimeBooking is not an option.
Note 2, I've used one repository (dao) for brevity, in real application you might want to inject two different repositories into the service.

OData (Olingo) "inhibit" endpoint

My question is about what is best way to inhibit an endpoint that is automatically provided by Olingo?
I am playing with a simple app based on Spring boot and using Apache Olingo.On short, this is my servlet registration:
#Configuration
public class CxfServletUtil{
#Bean
public ServletRegistrationBean getODataServletRegistrationBean() {
ServletRegistrationBean odataServletRegistrationBean = new ServletRegistrationBean(new CXFNonSpringJaxrsServlet(), "/user.svc/*");
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("javax.ws.rs.Application", "org.apache.olingo.odata2.core.rest.app.ODataApplication");
initParameters.put("org.apache.olingo.odata2.service.factory", "com.olingotest.core.CustomODataJPAServiceFactory");
odataServletRegistrationBean.setInitParameters(initParameters);
return odataServletRegistrationBean;
} ...
where my ODataJPAServiceFactory is
#Component
public class CustomODataJPAServiceFactory extends ODataJPAServiceFactory implements ApplicationContextAware {
private static ApplicationContext context;
private static final String PERSISTENCE_UNIT_NAME = "myPersistenceUnit";
private static final String ENTITY_MANAGER_FACTORY_ID = "entityManagerFactory";
#Override
public ODataJPAContext initializeODataJPAContext()
throws ODataJPARuntimeException {
ODataJPAContext oDataJPAContext = this.getODataJPAContext();
try {
EntityManagerFactory emf = (EntityManagerFactory) context.getBean(ENTITY_MANAGER_FACTORY_ID);
oDataJPAContext.setEntityManagerFactory(emf);
oDataJPAContext.setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
return oDataJPAContext;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
...
My entity is quite simple ...
#Entity
public class User {
#Id
private String id;
#Basic
private String firstName;
#Basic
private String lastName;
....
Olingo is doing its job perfectly and it helps me with the generation of all the endpoints around CRUD operations for my entity.
My question is : how can I "inhibit" some of them? Let's say for example that I don't want to enable the delete my entity.
I could try to use a Filter - but this seems a bit harsh. Are there any other, better ways to solve my problem?
Thanks for the help.
As you have said, you could use a filter, but then you are really coupled with the URI schema used by Olingo. Also, things will become complicated when you have multiple, related entity sets (because you could navigate from one to the other, making the URIs more complex).
There are two things that you can do, depending on what you want to achieve:
If you want to have a fined grained control on what operations are allowed or not, you can create a wrapper for the ODataSingleProcesor and throw ODataExceptions where you want to disallow an operation. You can either always throw exceptions (i.e. completely disabling an operation type) or you can use the URI info parameters to obtain the target entity set and decide if you should throw an exception or call the standard single processor. I have used this approach to create a read-only OData service here (basically, I just created a ODAtaSingleProcessor which delegates some calls to the standard one + overridden a method in the service factory to wrap the standard single processor in my wrapper).
If you want to completely un-expose / ignore a given entity or some properties, then you can use a JPA-EDM mapping model end exclude the desired components. You can find an example of such a mapping here: github. The mapping model is just an XML file which maps the JPA entities / properties to EDM entity type / properties. In order for olingo to pick it up, you can pass the name of the file to the setJPAEdmMappingModel method of the ODataJPAContext in your initialize method.

Why doesn't Mockito's when() get triggered?

I need to test a service class, but when I try to mock the dao class, it doesn't get triggered, thus not able to use ThenReturn().
I think that the problem is because I use an interface for my Dao and #Autowired in the service class (Spring MVC 3.1):
The interface:
public interface TestDao {
int createObject(Test test) throws NamingException;
}
The implementation:
#Repository
public class TestDaoImpl implements TestDao {
#Override
public int createObject(Test test) {
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new InsertNewTest(test), keyHolder);
return ((java.math.BigDecimal)keyHolder.getKey()).intValue();
}
}
The service:
public class RegTest {
#Autowired
TestDao testDao;
public int regTest(int .....) {
.
.
int cabotageId = testDao.createObject(test);
}
}
In the test I have:
#RunWith(MockitoJUnitRunner.class)
public class TestRegService {
#InjectMocks
private RegTest regTest = new RegTest();
#Mock
TestDao testDao;
#Test()
public void test() {
.
when(testDao.createObject(null)).thenReturn(100);
.
}
testDao.createObject(null) returns 0 (due to being mock'ed) and not 100 as I is trying to achieve.
Can anybody help, please?
Problem solved!
It was the passing test-object to createObject() that did not match. Using
testDao.createObject(any(Test.class))
did the trick!
If your test is actually passing a value to createObject, then when(testDao.createObject(null)... never gets matched. Rather than matching on null, you could match any instance of Test with testDao.createObject(any(Test.class))...
Also when you tried later to supply new Test() as the argument to match, it will literally try to match on that exact instance of Test, but presumably your real code is new-ing up a different one. So the use of Matchers.any(Test.class) as the parameter to match is the way to go.
Mockito injection mechanism don't know about Spring #Autowired or CDI #Inject annotations. It just tries to find the best candidate given the type and the name of the mock, and it can lookup private fields too. See the javadoc of #InjectMocks : http://docs.mockito.googlecode.com/hg/1.9.0/org/mockito/InjectMocks.html
The semantic you are using is correct, though if you are experiencing issues, I would rather look for incorrect interactions or incorrect arguments.
Are you sure the test variable in regTest.regTest(int...) is really null when passed to testDao.createObject(test) ?
I don't know if this is a typo in the example, but you have RegTest.regTest() calling createTest() rather than createObject(). Otherwise, I don't think #Autowired has anything to do with it, since your test itself is not running in a container with Spring management. If it is not a typo, and createTest is in fact a real and different method from createObject, then the default behaviour of a mocked object in Mockito is to return the appropriately-typed zero for numeric return types.
I think that you're right about the autowire not getting called. You could inject the dao yourself using the setTestDao() call instead. Mockito also supports spy which allows you to trace the objects code and just replace functions instead.

Resources