Why does #Cachable(...) work with #Bean return mock() but not with #MockedBean - spring-boot

Why does the cache get filled with Values when using
#Autowired
ServiceXY serviceXY
#TestConfiguration
static class AppDefCachingTestConfiguration {
#Bean
public ServiceXY ServiceXYMock() {
return mock(ServiceXY.class);
}
}
But not with
#MockBean
ServiceXY serviceXY
When using #MockBean i get a NullPointerException when accessing the cache values like that in my test:
#Autowired
ConcurrentMapCacheManager cmcm;
#Test
void anTest(){
when(serviceXY.methodThatFillsCache(anyString()).thenReturn("ABC");
serviceXY.methodThatFillsCache("TEST1");
cmcm.getCache("Cachename").get("TEST1",String.class).equals("ABC");
...
}

Caching is implemented using a proxy that intercepts calls to the cacheable method. When you use #MockBean, Spring Boot intentionally disables proxying. One consequence of this is that no caching is performed. Someone recently made the point that this isn't very well documented so we may update the docs in the future.
If you're want to test that caching is working as expected, you should either use a genuine implementation of your service, or create the mock yourself via a #Bean method as you have done in the first example in your question.

Related

MeterRegistry counter test case failing

I have implemented Micrometer Prometheus counter in my service by injecting MeterRegistry and incrementing the count as shown below, and I have written a test case as well, but when I am running the test case, I am getting:
"java.lang.NullPointerException: Cannot invoke
"io.micrometer.core.instrument.MeterRegistry.counter(String,
String[])" because "this.meterRegistry" is null".
Service file:
#Autowired
private MeterRegistry meterRegistry;
public void counterIncrement() {
meterRegistry.counter("test_count").increment();
}
Test case file:
#MockBean
private MeterRegistry registry;
#Test
void testCounter() {
// invoking counterIncrement();
}
How do you create your class under test?
Since the registry is never instantiated, something seems up with how you setup your test.
Check that you are using the #MockBean in the correct way. This will replace the bean in the application context and if you do not spin up a spring context in your test, it will not work. See this post for more info.
A different approach would be to use #Mock and inject the registry in the constructor, example:
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#Mock
private MeterRegistry registry;
private MyService myService;
#BeforeEach
void setup() {
myService = new MyService(registry);
}
#Test
void testCounter() {
var counter = mock(Counter.class);
given(registry.counter(any(String.class))).willReturn(counter);
myService.counterIncrement();
}
You can test metrics without Mockito using SimpleMeterRegistry
#Test
void testCounter() {
var meterRegistry = new SimpleMeterRegistry();
Metrics.addRegistry(meterRegistry);
// invoke counterIncrement();
var actual = meterRegistry.counter("test_count").count();
assertEquals(1.0d, actual);
}
Depending on which junit version you are using you need to add the annotation to your test class. Junit 5: #ExtendWith(MockitoExtension.class) or for Junit 4: #RunWith(MockitoJUnitRunner.class)
Depending on the test and the service there are several ways to deal with the missing MeterRegistry.
If you use a spring context in your test, try to use a test configuration to create the MeterRegistry bean.
If your test uses some Mock framework, you could mock the MeterRegistry as suggested by by #Hans-Christian.
If you simply make the member meterRegistry non-private. You could set it to a SimpleMeterRegistry in some setup method, anotated with #BeforeEach as suggested by #checketts in the comments.
If mocking the meter registry gets complicated, you could easily build and use some factory that provides the registry and mock this factory. A very easy factory will do, e.g. a spring #Component with an autowired MeterRegistry and some public getter for the factory.
You could use the factory method pattern as described in wikipedia to get the MeterRegistry, overwrite the factory method in a subclass of your service and use this subclass in the test. (Note that the gang of four did use a static factory method, you'll need a non-static method.)
I favour solution 3 but would use solution 1 whenever appropriate. I've added solutions 4 and 5 just because there might be some additional reasons and special cases that make these solutions a good choice. If so, I prefer 4 over 5.

Springboot test controller via WebClient instead of WebTestClient

I'm attempting something perhaps misguided but please help.
I would like to test springboot controller via #WebFluxTest. However I would like to use WebClient instead of WebTestClient. How can this be done?
So far, I managed to use reflection to get ExchangeFunction out of WebTestClient and assign it to WebClient - and it works! Calls are made, controller responds - wonderful. However I don't think this is good approach. Is there a better way?
Thank you.
Ideally, you should use a WebTestClient which is more of a convenience wrapper around the WebClient. Just like the TestRestTemplate is for a RestTemplate. Both allow a request to be created and you can easily make assertions. They exist to make your test life easier.
If you really want to use a WebClient instead of a WebTestClient and do the assertions manually (which means you are probably complicating things) you can use the WebClient.Builder to create one. Spring Boot will automatically configure one and you can simply autowire it in your test and call the build method.
#SpringBootTest
public void YourTest {
#Autowired
private WebClient.Builder webClientBuilder;
#Test
public void doTest() {
WebClient webClient = webClientBuilder.build();
}
}
The same should work with #WebFluxTest as well.
Ok, after much experimentation here is a solution to test springboot controller & filters via a mocked connection - no webservice, no ports and quick test.
Unfortunately I didn't work out how to do it via #WebFluxTest and WebClient, instead MockMvc can be used to achieve desired result:
#ExtendWith(SpringExtension.class)
#Import({SomeDependencyService.class, SomeFilter.class})
#WebMvcTest(controllers = SomeController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
#AutoConfigureMockMvc()
public class SomeControllerTest {
#MockBean
private SomeDependencyService someDependencyService;
#Autowired
private MockMvc mockMvc;
private SomeCustomizedClient subject;
#BeforeEach
public void setUp() {
subject = buildClient();
WebClient webClient = mockClientConnection();
subject.setWebClient(webClient);
}
private WebClient mockClientConnection() {
MockMvcHttpConnector mockMvcHttpConnector = new MockMvcHttpConnector(mockMvc);
WebClient webClient = WebClient.builder().clientConnector(mockMvcHttpConnector).build();
return webClient;
}
#Test
public void sample() {
when(SomeDependencyService.somePersistentOperation(any(), any())).thenReturn(new someDummyData());
SomeDeserializedObject actual = subject.someCallToControllerEndpoint("example param");
assertThat(actual.getData).isEquals("expected data");
}
}
Now it is possible to test your customized client (for example if you have internal java client that contains few important customization like security, etags, logging, de-serialization and uniform error handling) and associated controller (and filters if you #import them along) at the cost of a unit test.
You do NOT have to bring up entire service to verify the client and controller is working correctly.

Spring #Cachable method within the same class (self-invocation, proxy issue) - What is the best way to solve it?

I'm trying to call a #Cacheable method from within the same class.
And it didn't work. Because of:
In proxy mode (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 that calls another method of the target object) does not lead to actual caching at runtime even if the invoked method is marked with #Cacheable. Consider using the aspectj mode in this case. Also, the proxy must be fully initialized to provide the expected behavior, so you should not rely on this feature in your initialization code (that is, #PostConstruct).
It means, #Cachable(also #Transactional) works by proxy classes which is Spring AOP in. a internal call in the same class make call by 'this' instead of proxy classes.
To solve the problem, I should call a method by proxy or using AspectJ(another AOP).
So, I found 4 solutions.
What is your choice? and why others are not recommended?
Please, share your opinion!
using AspectJ (another AOP)
get the Bean from ApplicationContext and use it
#Service
public class UserService implements Service {
#Autowired
private ApplicationContext applicationContext;
private Service self;
#PostConstruct
private void init() {
self = applicationContext.getBean(UserService.class);
}
}
self-autowiring using #Resource //since Spring 4.3
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
make the Bean scope of the class as 'prototype' instead of 'singleton'
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
I'm a newbie in spring :)
Actually, I choose the 4th solution, but I felt it isn't a good way. because I just need to call the caching method by proxy, and it make several beans to achieve it.
After reading articles, I think AspectJ is the best choice. It looks cool, Spring recommends it, and many people also recommend too.
But I don't understand how to AspectJ works (I will study) and I also don't know why others is not recommended.
references
Spring Cache #Cacheable - not working while calling from another method of the same bean
Spring cache #Cacheable method ignored when called from within the same class
https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring
https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#cache

Order of Autowiring in SpringBoot

I would like Spring to inject Beans in a given order. I faced a strange issue with the code below. Both setAuthenticationProvider() and configureAuthManager() methods are injected in a random order. If configureAuthManager() is injected first, then during the login I get a NPE. Otherwise it works well.
So I want to force Spring to keep the order. I tried to annotate setAuthenticationProvider with Order(1) and the latter with Order(2) - did not help (why?). Then I also tried to to use #DependsOn annotation but it also didn't solve my problem.
#Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
// #Order(1) - does not help
#Autowired
#Qualifier("daoAuthenticationProvider")
public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
System.out.println("1");
this.authenticationProvider = authenticationProvider;
}
// #Order(2) - does not help
// #DependsOn("daoAuthenticationProvider") - does not help either
#Autowired
public void configureAuthManager(AuthenticationManagerBuilder authenticationManagerBuilder) {
System.out.println("2");
authenticationManagerBuilder.authenticationProvider(authenticationProvider);
}
[...]
}
The ordering of the bean creation shouldn't matter. If you need dependencies to be injected before you can call a method either
Use constructor based dependency injecetion
Add the argument to the method that needs it and use #Autowired (or #Bean if it creates a bean).
Either way Spring will be able to figure out in which order to create the beans to satisfy all the dependencies at the right time.

Spring service with cacheable methods gets initialized without cache when autowired in Shiro realm

After spending 2 days on this issue I really can't make any more progress on my own. I am working on a standard web application with Spring for dependency injection and the likes. I am also using Spring to cache several expensive methods I use a lot.
After I introduced Apache Shiro for the security layer, I was experiencing a strange issue where #Cacheable methods in a certain service no longer got cached. To this point, I was able to strip the problem down to its core, but there's still a lot of code for you to look at - sorry for that...
First, I configure all relevant packages (all classes shown in the following are in one of those).
#Configuration
#ComponentScan(basePackages = {
"my.package.config",
"my.package.controllers",
"my.package.security",
"my.package.services",
})
public class AppConfiguration {
}
Here is the configuration file for caching.
#Configuration
#EnableCaching
public class CacheConfiguration {
#Bean(name = "cacheManager")
public SimpleCacheManager cacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
simpleCacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("datetime")
));
return simpleCacheManager;
}
}
For my minimal example, I am using a very simple service that only returns the current timestamp. The Impl class is as simple as you would imagine.
public interface DateService {
#Cacheable("datetime")
LocalDateTime getCurrent();
}
I inject this service into a controller.
#Controller
#RequestMapping("/v1/date")
public class DateController {
#Autowired
DateService dateService;
#RequestMapping(value = "/current", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<String> getCurrent() {
Subject s = SecurityUtils.getSubject();
s.login(new MyToken());
return new ResponseEntity<>(dateService.getCurrent().toString(), HttpStatus.OK);
}
}
The application is set up and started via Jetty, and everything works as expected so far. When calling <api-url>/v1/date/current for the first time the current timestamp is returned, but afterwards one always receives the cached result.
Now, I introduce Shiro with yet another config file.
#Configuration
public class ShiroSecurityConfiguration {
#Bean
#Autowired
public DefaultSecurityManager securityManager(MyRealm realm) {
List<Realm> realms = new ArrayList<>();
// MyToken is a static stub for this example
realm.setAuthenticationTokenClass(MyToken.class);
realms.add(realm);
DefaultSecurityManager manager = new DefaultSecurityManager(realms);
SecurityUtils.setSecurityManager(manager);
return manager;
}
// other Shiro related beans that are - at least to me - irrelevant here
// EDIT 2: I figured out that the described problem only occurs with this bean
// (transitively depending on DateService) in the application
// the bean is required for annotations such as #RequiresAuthentication to work
#Bean
#Autowired
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Finally, here comes the realm which also depends on my service.
#Component
public class MyRealm extends AuthenticatingRealm {
private static final String REALM_NAME = "MyRealm";
#Autowired
private DateService dateService;
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("User authenticated at "+dateService.getCurrent());
return new SimpleAuthenticationInfo("",token.getCredentials(),REALM_NAME);
}
}
With that, the caching is broken in my entire application. There is no error message, it just doesn't use the cache anymore. I was able to implement a workaround, but I am now seeking for a better solution and maybe also some advice to better understand the essence of my issue. So, here comes the workaround.
#Component
public class MyRealm extends AuthenticatingRealm {
private static final String REALM_NAME = "MyRealm";
private DateService dateService;
#Autowired
private ApplicationContext applicationContext;
private void wireManually() {
if (dateService == null) {
dateService = applicationContext.getBean(DateService.class);
}
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
wireManually();
System.out.println("User authenticated at "+dateService.getCurrent());
return new SimpleAuthenticationInfo("",token.getCredentials(),REALM_NAME);
}
}
Now it's back to working, and I was able to debug the reason for that. Shiro and hence MyRealm gets initialized very early, even before the whole caching with my SimpleCacheManager and all the related stuff (cacheInterceptor etc.) is loaded. Therefore, there is no proxy to wrap around the service when it gets initialized before the realm when using #Autowired. With the workaround shown above, the service is not injected before everything is set up properly and the first request is being served, and then there is no problem.
Simply put, as soon as I make MyRealm dependent on DateService (annotating the last version of MyRealm with #DependsOn("dateServiceImpl") is enough to break the application) it gets initialized too early (i.e. before caching is set up).
So I would need to either postpone the initialization of MyRealm, but I don't know how to do that. I tried #DependsOn("cacheManager"), but that doesn't help as the other beans required for caching are loaded later nonetheless. Or - which is the same from another perspective - I could make sure the whole caching infrastructure (I am not enough of an expert to describe it in detail) is initialized earlier. Unfortunately, I also don't know how to do that...
Thanks in advance to everyone who made it to this point. Looking forward to any input, no matter if it's an idea to fix the code in a better way or an explanation why exactly Spring can't get this right on its own.
I finally figured out what the problem is and can at least explain its cause in more detail, even though my proposed solution is still a bit hacky.
Enabling the caching aspect in Spring introduces a org.springframework.cache.interceptor.CacheInterceptor, which is essentially an org.aopalliance.aop.Advice used by a org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor that implements org.springframework.aop.Advisor.
The org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor I introduced for Shiro is another Advisor which transitively depends on the DateService via DefaultSecurityManager and MyRealm.
So I have two Advisors for two different aspects - Caching and Security - of which the one for security is initialized first. In fact, whenever I introduce any Advisor dependent on DateService - even if its only a dummy implementation as in the following example - the caching doesn't work anymore for the same reason as it was broken when adding Shiro. This causes the DateService to be loaded before the caching aspect is ready, so it cannot be applied.
#Bean
#Autowired
public Advisor testAdvisor(DateService dateService) {
return new StaticMethodMatcherPointcutAdvisor() {
#Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
Hence, the only proper fix for that is to change the order of aspect initialization. I am aware of the #Order(Ordered.LOWEST_PRECEDENCE) respectively #Order(Ordered.HIGHEST_PRECEDENCE) annotation for the case the multiple Advisors are applicable at a specific joinpoint, but this is not the case for me so this doesn't help. The order of initialization matters for other reasons.
Adding the following code in DateServiceImpl actually solves the problem:
#Autowired
BeanFactoryCacheOperationSourceAdvisor waitForCachingAspect;
With that, the service always waits for the cache before it can be initialized even though this dependency is not used anywhere in the implementation. So now everything is working as it should because the dependency tree now includes Shiro --> DateService --> Cache which makes the Shiro Advisor wait long enough.
It is still not as nice and clean as I would like it to be, but nevertheless, I think this explanation helps to understand the core of the problem and "How can I change the order in which Advisors are initialized in Spring" is a separate question I posted here.
Since Spring 4, #Lazy can be used to achieve the same behavior as in the original question in a more declarative way (see Spring 4 JavaDoc and compare it with earlier versions).
Tested this and it works.
#Component
public class MyRealm extends AuthenticatingRealm {
private static final String REALM_NAME = "MyRealm";
#Autowired
#Lazy
private DateService dateService;
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("User authenticated at "+dateService.getCurrent());
return new SimpleAuthenticationInfo("",token.getCredentials(),REALM_NAME);
}
}

Resources