spring boot values not substituted by gradle during Integration test - spring

I don't know if this is a spring boot or gradle issue
I am using spring boot 1.4 to build Restful Services.
I have a StatusController class which gives status of service. This includes service name,buildtime,environmenet and version.
Here is my Controller
#RestController
public class StatusController
{
#Value("${service.version: not configured}")
private String version;
#Value("${service.name: not configured}")
private String appName;
#Value("${service.env: not configured}")
private String env;
#Value("${service.timestamp: not configured}")
private String buildDate;
#RequestMapping(value = "/status", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public String getStatus() throws JsonProcessingException {
Map<String,String> map = new HashMap<>();
map.put("version", version);
map.put("appName", appName);
map.put("env", env);
map.put("buildDate", buildDate);
return new ObjectMapper().writeValueAsString(map);
}
}
src/main/resources/application.properties
service.name=${name}
service.version=${version}
service.timestamp=${timestamp}
build.gradle (only relevant section)
processResources {
expand project.properties
}
def buildTime() {
def today = new Date()
def formattedDate = today.format('MM-dd-yyyy HH:mm:ss z')
return formattedDate
}
ext.timestamp=buildTime()
I have version defined in gradle.properties
version=1.0.0-SNAPSHOT.
when I start my service (gradlew bootRun) and I hit /status, I see all the values injected (not the default values of "not configured". However, in my tests, see the values not being substituted but default values injected.
Here is my test class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
#Test
public void contextLoads() {
System.out.println("test passed");
}
#Value("${service.version: not configured}")
private String version;
#Value("${service.name: not configured}")
private String appName;
#Value("${service.env: not configured}")
private String env;
#Value("${service.timestamp: not configured}")
private String buildDate;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void exampleTest() throws Exception {
String lifeCheck = this.restTemplate.getForObject("/status", String.class);
System.out.print(lifeCheck);
}
}
using debugger, I confirmed the values are not substituted in the test class.
but also not substituted in controller
Here is the output from running above test
{"appName":" not configured","buildDate":" not configured","env":" not configured","version":" not configured"}

Don't forget that there's a separate task in Gradle called processTestResources

Place the application.properties in your src/test/resources/
The test classes cannot see your main resources. The values will be substituted if you place it in your test resources folder. :)

Related

Springboot test not loading properties from application.yml

I have a springboot test case trying to test a method which has some dependency on env properties.
#SpringBootTest(classes = ABCApi.class)
#AutoConfigureMockMvc
class sampleTest {
#Autowired
private WebApplicationContext context;
protected MockMvc mockMvc;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
#InjectMocks
private EmployeeServiceImpl employeeServiceImpl;
#Test
void testEmployeeServiceImpl() {
String summary = "summary";
String description = "description";
String response = employeeServiceImpl.createIssue(summary, description);
verify(EmployeeService, times(1)).createIssue(summary, description);
}
}
Note: I don't need to test the response. All I need is to make sure that the method is called. The response will be null because of some unavailable parameters.
Below is the EmployeeServiceImpl code which has all properties listed. I have the application.yml file in src/test/resources, which is the right location. But while debugging the test case above, the below class is not loading any properties from application.yml sitting in test folder structure.
#Service
public class EmployeeServiceImpl {
#Value("${emp.username}")
private String username;
#Value("${emp.password}")
private String password;
#Value("${emp.url}")
private String url;
#Override
public String createIssue(String summary, String description) {
EmpRestClient client = setEmpClient();
EmpSuperClient empSuperClient = client.getProject();
IssueInput newIssue = buildNewIssueInput(description, summary);
return empSuperClient.createIssue(newIssue).claim().getKey();
}
private EmpRestClient setEmpClient() {
return new EmpRestClientFactory()
.createWithBasicHttpAuthentication(URI.create(url), username, password);
}
I know that these properties are not supposed to be in service layer and instead should be loaded as a bean in config class which will be a future improvement.
How do I fix that test case?
Use constructor injection to inject dependencies. It makes code easy to test.
private final String username;
private final String password;
private final String url;
public EmployeeServiceImpl (#Value("${emp.username}") final String username, #Value("${emp.password}") final String password,
#Value("${emp.url}") String url) {
this.username = username;
this.password = password;
this.url = url;
}
Now you can use the constructor to create EmployeeServiceImpl in your test class.
E.g.
new EmployeeServiceImpl ("username", "password", "http://localhost:8080")

Springboot test issue: Mockito is returning empty object

I am trying to build a prototype that is using gradle as build tool and openjdk-11. This prototype will build a rest-api on springboot framework.
My module is working fine with rest-api call and returning expected result. However, as I am now trying to write a test for the rest api, the test is failing as Mockito is returning empty object. Would appreciate any insight on how should I write a test for this rest-api or what to do to fix it.
My controller:
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
GreetingService service;
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return service.getGreetings(0L, String.format(template, name));
}
}
The service:
#Service
public class GreetingService {
public Greeting getGreetings() {
return new Greeting(1L, "Hello World");
}
public Greeting getGreetings(Long id, String name) {
return new Greeting(id, name);
}
}
The Model:
#Builder
#Data
#RequiredArgsConstructor
#JsonDeserialize(builder = Greeting.class)
public class Greeting {
#NonNull
private Long id;
#NonNull
private String content;
}
The main class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I executed this by :
gradle bootrun
And then from browser, tried :
http://localhost:8080/greeting
And that returned:
{"id":0,"content":"Hello, World!"}
Again tried:
http://localhost:8080/greeting?name=Patty
and that returned:
{"id":0,"content":"Hello, Patty!"}
Now, I was trying to write test to validate api calls similar to the above calls programatically. So I tried:
#RunWith(MockitoJUnitRunner.class)
public class GreetingControllerTest {
private MockMvc mockMvc;
#Mock
private GreetingService service;
#InjectMocks
private GreetingController controller
#Test
public void testGreeting() throws Exception {
Greeting greeting = new Greeting(0L,"Patty!");
String expectedResponse = "{\"id\":0,\"content\":\"Hello, Patty!\"}";
//JacksonTester.initFields(this, new ObjectMapper());
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.build();
Mockito.when(service.getGreetings(0L,"Patty")).thenReturn(greeting);
MockHttpServletResponse response = mockMvc
.perform(get("/greeting?name=Patty")
.contentType(MediaType.ALL))
.andReturn()
.getResponse();
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
assertThat(response.getContentAsString()).isEqualTo(expectedResponse)
}
}
The error msg is :
org.junit.ComparisonFailure:
Expected :"{"id":0,"content":"Hello, Patty!"}"
Actual :""
Failing from this line:
assertThat(response.getContentAsString()).isEqualTo(expectedResponse)
Thanks in advance.
This helped me understanding: Mockito - thenReturn always returns null object
I changed the Mockito.when part to :
Mockito.when(service.getGreetings(Mockito.anyLong(),Mockito.anyString())).thenReturn(greeting);
and it worked

Using #RestClientTest in spring boot test

I want to write a simple test using #RestClientTest for the component below (NOTE: I can do it without using #RestClientTest and mocking dependent beans which works fine.).
#Slf4j
#Component
#RequiredArgsConstructor
public class NotificationSender {
private final ApplicationSettings settings;
private final RestTemplate restTemplate;
public ResponseEntity<String> sendNotification(UserNotification userNotification)
throws URISyntaxException {
// Some modifications to request message as required
return restTemplate.exchange(new RequestEntity<>(userNotification, HttpMethod.POST, new URI(settings.getNotificationUrl())), String.class);
}
}
And the test;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}
But i get error that No qualifying bean of type 'org.springframework.web.client.RestTemplate' available. Which is right of course as i havn't mocked it or used #ContextConfiguration to load it.
Isn't #RestClientTest configures a RestTemplate? or i have understood it wrong?
Found it! Since i was using a bean that has a RestTemplate injected directly, we have to add #AutoConfigureWebClient(registerRestTemplate = true) to the test which solves this.
This was in the javadoc of #RestClientTest which i seem to have ignored previously.
Test which succeeds;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
#AutoConfigureWebClient(registerRestTemplate = true)
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}

#CachePut does not work in #Configuration for pre cache

I was trying to use spring stater-cache in spring boot 1.3.5, everything works fine except pre load cache in #Configuration class.
Failed tests:
CacheTest.testCacheFromConfig: expected:<n[eal]> but was:<n[ot cached]>
Please take a look at the code as below, if you met this before, please share it with me :)
#Component
public class CacheObject{
#CachePut(value = "nameCache", key = "#userId")
public String setName(long userId, String name) {
return name;
}
#Cacheable(value = "nameCache", key = "#userId")
public String getName(long userId) {
return "not cached";
}
}
#Component
public class CacheReference {
#Autowired
private CacheObject cacheObject;
public String getNameOut(long userId){
return cacheObject.getName(userId);
}
}
#Configuration
public class SystemConfig {
#Autowired
private CacheObject cacheObject;
#PostConstruct
public void init(){
System.out.println("------------------");
System.out.println("-- PRE LOAD CACHE BUT DIDN'T GET CACHED");
System.out.println("------------------");
cacheObject.setName(2, "neal");
cacheObject.setName(3, "dora");
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BootElastic.class)
#WebAppConfiguration
public class CacheTest {
#Autowired
private CacheObject cacheObject;
#Autowired
private CacheReference cacheReference;
#Test
public void testCache(){
String name = "this is neal for cache test";
long userId = 1;
cacheObject.setName(userId, name);
// cacheObject.setName(2, "neal"); // this will make test success
String nameFromCache = cacheReference.getNameOut(userId);
System.out.println("1" + nameFromCache);
Assert.assertEquals(nameFromCache, name);
}
#Test
public void testCacheFromConfig(){
String nameFromCache = cacheReference.getNameOut(2);
System.out.println("4" + nameFromCache);
Assert.assertEquals(nameFromCache, "neal");
}
}
#PostConstruct methods are called right after all postProcessBeforeInitialization() BeanPostProcessor methods invoked, and right before postProcessAfterInitialization() invoked. So it is called before there is any proxy around bean, including one, putting values to cache.
The same reason why you can't use #Transactional or #Async methods in #PostConstruct.
You may call it from some #EventListener on ContextRefreshedEvent to get it working

TestRestTemplate postForEntity does not send the requestbody Spring Boot 1.4

#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ServerApplication.class)
public class ExampleTest {
public static final String EXAMPLE = "/example";
//this is the TestRestTemplate used
#Autowired
TestRestTemplate testRestTemplate;
//this is the test
#Test
public void testExample() {
String s = "asd";
Object response = testRestTemplate.postForEntity(EXAMPLE, s, String.class ,Collections.emptyMap());
}
}
This is the tested endpoint:
#RestController
public class ServerController {
#ResponseBody
#PostMapping("/example")
public String exampleEndpoint (String in) {
return in;
}
}
#SpringBootApplication
#ComponentScan("com.company.*")
public class ServerApplication {
etc.
When I debug it, at the exampleEndpoint the in parameter is always null.
I'm using Spring Boot 1.4 Spring 4.3.2
I changed the names and removed all the company stuff, but otherwise this is how it is, it works in the sense that it hits the endpoint, it just that the request doesn't go through.
You need to annotate the argument in your method exampleEndpoint with RequestBody like so:
public String exampleEndpoint (#RequestBody String in) {
return in;
}

Resources