-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat : add test using @RestClientTest
#1330
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,89 +1,52 @@ | ||||||||||||||||||||||||
package com.example.ultimateredis.config; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
import io.lettuce.core.RedisURI; | ||||||||||||||||||||||||
import java.time.Duration; | ||||||||||||||||||||||||
import java.util.HashMap; | ||||||||||||||||||||||||
import java.util.Map; | ||||||||||||||||||||||||
import java.util.Map.Entry; | ||||||||||||||||||||||||
import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer; | ||||||||||||||||||||||||
import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer; | ||||||||||||||||||||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||||||||||||||||||||||
import org.springframework.cache.Cache; | ||||||||||||||||||||||||
import org.springframework.cache.CacheManager; | ||||||||||||||||||||||||
import org.springframework.cache.annotation.CachingConfigurer; | ||||||||||||||||||||||||
import org.springframework.cache.annotation.EnableCaching; | ||||||||||||||||||||||||
import org.springframework.cache.interceptor.CacheErrorHandler; | ||||||||||||||||||||||||
import org.springframework.context.annotation.Bean; | ||||||||||||||||||||||||
import org.springframework.context.annotation.Configuration; | ||||||||||||||||||||||||
import org.springframework.context.annotation.Primary; | ||||||||||||||||||||||||
import org.springframework.data.redis.cache.RedisCacheConfiguration; | ||||||||||||||||||||||||
import org.springframework.data.redis.cache.RedisCacheManager; | ||||||||||||||||||||||||
import org.springframework.data.redis.connection.RedisConnectionFactory; | ||||||||||||||||||||||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration; | ||||||||||||||||||||||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; | ||||||||||||||||||||||||
import org.springframework.data.redis.core.RedisTemplate; | ||||||||||||||||||||||||
import org.springframework.data.redis.serializer.RedisSerializationContext; | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Configuration(proxyBeanMethods = false) | ||||||||||||||||||||||||
@EnableConfigurationProperties(CacheConfigurationProperties.class) | ||||||||||||||||||||||||
@EnableCaching | ||||||||||||||||||||||||
@Slf4j | ||||||||||||||||||||||||
public class CacheConfig implements CachingConfigurer { | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Bean | ||||||||||||||||||||||||
@Primary | ||||||||||||||||||||||||
RedisCacheConfiguration defaultCacheConfig() { | ||||||||||||||||||||||||
RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer( | ||||||||||||||||||||||||
CacheConfigurationProperties cacheConfigurationProperties) { | ||||||||||||||||||||||||
RedisCacheGZIPSerializer serializerGzip = new RedisCacheGZIPSerializer(); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return RedisCacheConfiguration.defaultCacheConfig() | ||||||||||||||||||||||||
.serializeValuesWith( | ||||||||||||||||||||||||
RedisSerializationContext.SerializationPair.fromSerializer(serializerGzip)); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Bean | ||||||||||||||||||||||||
LettuceConnectionFactory redisConnectionFactory(CacheConfigurationProperties properties) { | ||||||||||||||||||||||||
log.info( | ||||||||||||||||||||||||
"Redis (/Lettuce) configuration enabled. With cache timeout {} seconds.", | ||||||||||||||||||||||||
properties.getTimeoutSeconds()); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
RedisStandaloneConfiguration redisStandaloneConfiguration = | ||||||||||||||||||||||||
new RedisStandaloneConfiguration(); | ||||||||||||||||||||||||
redisStandaloneConfiguration.setHostName(properties.getRedisHost()); | ||||||||||||||||||||||||
redisStandaloneConfiguration.setPort(properties.getRedisPort()); | ||||||||||||||||||||||||
return new LettuceConnectionFactory(redisStandaloneConfiguration); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Bean | ||||||||||||||||||||||||
RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) { | ||||||||||||||||||||||||
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>(); | ||||||||||||||||||||||||
redisTemplate.setConnectionFactory(cf); | ||||||||||||||||||||||||
return redisTemplate; | ||||||||||||||||||||||||
return builder -> { | ||||||||||||||||||||||||
builder.cacheDefaults() | ||||||||||||||||||||||||
.disableCachingNullValues() | ||||||||||||||||||||||||
.serializeValuesWith( | ||||||||||||||||||||||||
RedisSerializationContext.SerializationPair.fromSerializer( | ||||||||||||||||||||||||
serializerGzip)); | ||||||||||||||||||||||||
cacheConfigurationProperties | ||||||||||||||||||||||||
.getCacheExpirations() | ||||||||||||||||||||||||
.forEach( | ||||||||||||||||||||||||
(cacheName, timeout) -> | ||||||||||||||||||||||||
builder.withCacheConfiguration( | ||||||||||||||||||||||||
cacheName, | ||||||||||||||||||||||||
RedisCacheConfiguration.defaultCacheConfig() | ||||||||||||||||||||||||
.entryTtl(Duration.ofSeconds(timeout)))); | ||||||||||||||||||||||||
}; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Bean | ||||||||||||||||||||||||
RedisCacheConfiguration defaultCacheConfiguration(CacheConfigurationProperties properties) { | ||||||||||||||||||||||||
return createCacheConfiguration(properties.getTimeoutSeconds()); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
private RedisCacheConfiguration createCacheConfiguration(long timeoutSeconds) { | ||||||||||||||||||||||||
return RedisCacheConfiguration.defaultCacheConfig() | ||||||||||||||||||||||||
.entryTtl(Duration.ofSeconds(timeoutSeconds)); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Bean | ||||||||||||||||||||||||
CacheManager cacheManager( | ||||||||||||||||||||||||
RedisConnectionFactory redisConnectionFactory, | ||||||||||||||||||||||||
CacheConfigurationProperties properties, | ||||||||||||||||||||||||
RedisCacheConfiguration defaultCacheConfiguration) { | ||||||||||||||||||||||||
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>(); | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
for (Entry<String, Long> cacheNameAndTimeout : | ||||||||||||||||||||||||
properties.getCacheExpirations().entrySet()) { | ||||||||||||||||||||||||
cacheConfigurations.put( | ||||||||||||||||||||||||
cacheNameAndTimeout.getKey(), | ||||||||||||||||||||||||
createCacheConfiguration(cacheNameAndTimeout.getValue())); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
return RedisCacheManager.builder(redisConnectionFactory) | ||||||||||||||||||||||||
.cacheDefaults(defaultCacheConfiguration) | ||||||||||||||||||||||||
.withInitialCacheConfigurations(cacheConfigurations) | ||||||||||||||||||||||||
.build(); | ||||||||||||||||||||||||
LettuceClientConfigurationBuilderCustomizer lettuceClientConfigurationBuilderCustomizer( | ||||||||||||||||||||||||
CacheConfigurationProperties properties) { | ||||||||||||||||||||||||
return clientConfigurationBuilder -> | ||||||||||||||||||||||||
clientConfigurationBuilder.apply(RedisURI.create(properties.getRedisURI())); | ||||||||||||||||||||||||
Comment on lines
+46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation for the Redis URI. The function should validate the Redis URI to ensure it is correctly formatted and non-null before applying it to the client configuration builder. + if (properties.getRedisURI() == null || properties.getRedisURI().isEmpty()) {
+ throw new IllegalArgumentException("Redis URI must not be null or empty");
+ }
return clientConfigurationBuilder ->
clientConfigurationBuilder.apply(RedisURI.create(properties.getRedisURI())); Committable suggestion
Suggested change
|
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
cache.timeout=60 | ||
cache.cacheExpirations.myControlledCache=180 | ||
cache.redis-uri=redis://localhost:6379 | ||
|
||
spring.mvc.problemdetails.enabled=true | ||
spring.threads.virtual.enabled=true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.example.restclient.bootrestclient.services; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; | ||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; | ||
|
||
import com.example.restclient.bootrestclient.config.RestClientConfiguration; | ||
import com.example.restclient.bootrestclient.model.response.PostDto; | ||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.util.Optional; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.test.web.client.MockRestServiceServer; | ||
|
||
@RestClientTest( | ||
components = {PostService.class, HttpClientService.class, RestClientConfiguration.class}) | ||
class PostServiceTest { | ||
|
||
@Autowired private MockRestServiceServer mockRestServiceServer; | ||
|
||
@Autowired private ObjectMapper objectMapper; | ||
|
||
@Autowired private PostService postService; | ||
|
||
@Test | ||
void findPostById() throws JsonProcessingException { | ||
|
||
PostDto postDto = new PostDto(1000L, 583L, "JunitTitle", "Response from RestClientTest"); | ||
this.mockRestServiceServer | ||
.expect(requestTo("https://jsonplaceholder.typicode.com/posts/1")) | ||
.andExpect(method(HttpMethod.GET)) | ||
.andExpect(header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) | ||
.andExpect(header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) | ||
.andExpect(header("apiKey", "123456")) | ||
.andRespond( | ||
withSuccess( | ||
objectMapper.writeValueAsString(postDto), | ||
MediaType.APPLICATION_JSON)); | ||
|
||
Optional<PostDto> optionalPostDto = postService.findPostById(1L); | ||
|
||
assertThat(optionalPostDto).isPresent(); | ||
assertThat(optionalPostDto.get().title()).isEqualTo("JunitTitle"); | ||
|
||
mockRestServiceServer.verify(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting magic numbers and adding error handling.
The function uses a hardcoded value for the TTL duration, which could be extracted to a constant or configuration property. Additionally, consider adding error handling in case the cache configuration properties are invalid or missing.
Define
DEFAULT_TTL
as a constant or retrieve it from configuration properties.