How to access application.property variable value into test class - spring-data-jpa

I am using spring data jpa for creating microservices. I am trying to write test cases for controller. Now In controller url I am accessing InstituteIdentifier variable value from application.property file. In AccountController class I am getting InstituteIdentifier variable value in url
But I am not able to access that InstituteIdentifier variable value in test class url. I tried using $ InstituteIdentifier in test case url but value is not injecting and I am getting 404 test case failure error.
Instead of accessing variable in url if I am hard coding InstituteIdentifier value then test case running without any error. But I don't want to hard code.
application.property file I am having in src/main directory. Can any one tell me how to access that application.property file variable in test class? or I need to create separate property file for test also.
AccountController
#RestController
#CrossOrigin(origins = "${crossOrigin}")
#RequestMapping("/spacestudy/${InstituteIdentifier}/admin/account")
public class AccountController {
#Autowired
AccountService accService;
#GetMapping("/loadAcctLocationList")
public ResponseEntity<List<Account>> findLocation() throws Exception{
return ResponseEntity.ok(accService.findLocation());
}
TestAccountController
#RunWith(SpringRunner.class)
#WebMvcTest(value=AccountController.class)
public class TestAccountController {
#Autowired
private MockMvc mockMvc;
#MockBean
private AccountService accountService;
#Test
public void findLocationTest() throws Exception {
Account account = new Account();
account.setsLocation("Test1");
List<Account> accountObj = new ArrayList<Account>();
accountObj.add(account);
Mockito.when(accountService.findLocation()).thenReturn(accountObj);
mockMvc.perform(get("/spacestudy/$ InstituteIdentifier/admin/account/loadAcctLocationList"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].sLocation", is("Test1")))
.andExpect(jsonPath("$.*",Matchers.hasSize(1)));
for(Account result: accountObj) {
assertEquals("Test1", result.sLocation);
}
}

You have to inject the property using the Value annotation and then use it to build the URL.
#Value("${InstituteIdentifier}")
private String instituteIdentifier;

Related

JUnit5 - Rest API controller having custom spring validator is failing

I have a controller that accepts path parameter called 'jobName'. The #ValidateJobName is the custom validator that validates the user input. If the input is wrong then it throws the error below
"Invalid Job name, valid job names are: vendor, service, product,
pricing, currency, contract"
The issue I am facing is that, when I am testing my rest controller API the test case always fails by returning the above error even when the job name is one of the acceptable values but when I remove #ValidateJobName custom annotation from the controller my test cases gets passed.
The #ValidateJobName and Controller works all good when triggered from Postman client but when I do unit testing the test case fails.
I have tried lot of blogs and googled but could not get a solution, Below are my Controller and JUnit testcase.
Please help!
JobController.java
#Validated
#Slf4j
#RestController
public class JobController {
#Autowired
ReportService reportService;
#Autowired
ReportConfig reportConfig;
#RequestMapping(value = "/importjob/{jobName}", method = RequestMethod.GET)
ResponseEntity<DataIntegrationResponse> getReport(#PathVariable #ValidateJobName String jobName) throws Exception {
log.info("Received a request to launch the " + jobName + " Job");
return reportService.getReport(jobName);
}
}
JobControllerTest.java
#ExtendWith(MockitoExtension.class)
#WebMvcTest(JobController.class)
#AutoConfigureMockMvc
public class JobControllerTest {
#MockBean
ReportService reportService;
#MockBean
ReportConfig rep;
#MockBean
JobMapping jmap;
#Autowired
public MockMvc mockMvc;
#Test
public void testGetReport() throws Exception {
String jobNameInput="vendor";
HttpStatus httpStatus = HttpStatus.OK;
String fitsReportName = "idex_fits_vendor.csv";
String jobName = "WFitsVendorJob";
String jobStatus = "STARTED";
Long jobInstanceId = 1022L;
String message = "WFitsVendorJob triggered successfully.";
DataIntegrationResponse response = new DataIntegrationResponse(LocalDateTime.now(), httpStatus, fitsReportName, jobName, jobStatus, jobInstanceId, message);
ResponseEntity<DataIntegrationResponse> responseEntity = new ResponseEntity<DataIntegrationResponse>(response, HttpStatus.OK);
Mockito.when(reportService.getReport(jobNameInput)).thenReturn(responseEntity);
mockMvc.perform(get("/importjob/{jobName}", "vendor")).andExpect(status().isOk());
}
JobNameValidator.java
#Component
public class JobNameValidator implements ConstraintValidator<ValidateJobName, String>{
#Autowired
private JobMapping jobMap;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value!=null && !jobMap.getMappings().containsKey(value)) { return false; }
return true;
}
}
ValidateJobName.java - interface
#Documented
#Constraint(validatedBy = JobNameValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
public #interface ValidateJobName {
String message() default "Invalid Job name, valid job names are: vendor, service, product, pricing, currency, contract";
Class<?>[] groups() default {};
Class<? extends Payload> [] payload() default {};
}
This is because you use a mock of JobMapping
#MockBean
JobMapping jmap;
Your JobNameValidator receive a Mock and doesn't know what to return when calling the containsKey method.
First solution is to tell what to do with this mock :
Mockito.when(jobMapping.getMappings()).thenReturn(// Map containing "vendor");
The second solution is to import your real JobMapping class instead of a mock:
#ExtendWith(MockitoExtension.class)
#WebMvcTest(JobController.class)
#AutoConfigureMockMvc
#Import(JobMapping.class)
class JobControllerTest {
// #MockBean
// JobMapping jmap;
}

Spring Boot o.s.web.servlet.pagenotfound

I am trying to call spring-boot rest controller but it throws o.s.web.servlet.pagenotfound spring boot, I have seen too many answers here but none of these helped me.
controller class is as below
#RestController
#RequestMapping("/users")
public class UsersController {
#Autowired
private UsersRepository usersRepository;
#GetMapping("/users")
public List<Users> getAllUsers() {
return usersRepository.findAll();
}
}
And application.yml is as below
spring.datasource.url = jdbc:mysql://localhost:3306/mydb?useSSL=false
spring.datasource.username=mine
spring.datasource.password=mine
kindly let me know if any further information required
I had the same problem, there should be a problem with your URL. You must be hitting the wrong URL.
I'm assuming you are using Postman for testing the GET Request.
Check if you are hitting through GET Request, and the format is JSON.
And try the below:
#RestController
#RequestMapping(value = "/")
public class UsersController {
#Autowired
private UsersRepository usersRepository;
#GetMapping("/users")
public List<Users> getAllUsers() {
return usersRepository.findAll();
}
}

Spring Boot: How to use #RestController with a class whose variables come from a properties file

So I'm new to Spring and I'm basically trying to make a REST service for the first time. Some of the data I'd like to return is some data from a properties file.
This is my configuration bean:
#Configuration
#PropertySource("classpath:client.properties")
public class PropertyConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer
propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
This is the class containing the info I want to return from the API. When I hover over the values, I can see that the property is being injected.
public class ProviderInfo {
#Value("${op.iss}") private String issuer;
#Value("${op.jwks_uri}") private String jwksURI;
#Value("${op.authz_uri}") private String authzURI;
#Value("${op.token_uri}") private String tokenURI;
#Value("${op.userinfo_uri}") private String userInfoURI;
// Getter methods
}
And this is the RestController
#RestController
public class ProviderInfoController {
#RequestMapping(value = "/provider-info", method = RequestMethod.GET)
public ProviderInfo providerInfo() {
return new ProviderInfo();
}
}
When I navigate to that endpoint, everything is null:
{"issuer":null,"jwksURI":null,"authzURI":null,"tokenURI":null,"userInfoURI":null}
Can anybody see what I'm doing wrong? Or if there is a better way to accomplish this in general?
Thanks!
The processing of the #Value annotations is done by Spring, so you need to get the ProviderInfo instance from Spring for the values to actually be set.
#RestController
public class ProviderInfoController {
#Autowired
private ProviderInfo providerInfo;
#RequestMapping(value = "/provider-info", method = RequestMethod.GET)
public ProviderInfo providerInfo() {
return providerInfo;
}
}
This also requires that Spring picks up and processes the ProviderInfo class.
Also, you need to add the ProviderInfo class to the Spring Bean life cycle using either #Component or #Service as follows:
#Component
public class ProviderInfo {
#Value("${op.iss}") private String issuer;
#Value("${op.jwks_uri}") private String jwksURI;
#Value("${op.authz_uri}") private String authzURI;
#Value("${op.token_uri}") private String tokenURI;
#Value("${op.userinfo_uri}") private String userInfoURI;
// Getter methods
}
Only then, you can use #Autowired inside ProviderInfoController class.

Content Type Not Set Junit REST POST test

I have a Spring Boot REST application. The unit tests for all of the GET requests are working perfectly; however, the POST requests are all returning
java.lang.AssertionError: Content type not set
Here is the controller:
#RestController
public class ClassificationController {
private IClassificationService classificationService;
#Autowired
public ClassificationController(IClassificationService classificationService) {
this.classificationService = classificationService;
}
#RequestMapping(value="/category", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public CategoryDTO createCategory(#RequestBody final CategoryDTO category) throws MctException {
return classificationService.createCategory(category);
}
The unit test I have is:
#RunWith(MockitoJUnitRunner.class)
public class ClassificationControllerTest {
#Mock
private IClassificationService classificationService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new ClassificationController(classificationService)).build();
}
#Test
public void createCategoryTest() throws Exception {
String jsonTask = String.format("{\"id\": \"2\",\"categoryName\": \"Category Name 2\"}");
MvcResult result = mockMvc.perform(post("/category")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonTask))
.andDo(MockMvcResultHandlers.print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().string(containsString("\"id\":2")))
.andExpect(content().string(containsString("\"categoryName\":\"Category Name 2\"")))
.andExpect(status().isCreated())
.andReturn();
}
I have also tried this with a CategoryDTO object instead of the String jsonTask with the same result.
I discovered it was just failing on that assertion because it was the first one, but it just wasn't returning anything from the endpoint. I am returning the content type because it is returning the object that is being inserted so a content type is valid. I ended up changing my test create the content JSON using an ObjectMapper and then I had to add an equals method on my domain object....once I added the equals method, the test passed. I didn't realize the mock framework used that method.
#Test
public void createClassTest() throws Exception {
String jsonInString = objectMapper.writeValueAsString(singleClass);
when(classificationService.createClass(5, singleClass)).thenReturn(singleClass);
MvcResult result = mockMvc.perform(post("/class/5")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonInString))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().string(containsString("\"id\":1")))
.andExpect(content().string(containsString("\"className\":\"Test Class Name 1\"")))
.andExpect(status().isCreated())
.andReturn();
verify(classificationService).createClass(5, singleClass);
}
Judging by the assertion error, it seems the endpoint is not returning a MediaType.APPLICATION_JSON_UTF8. Try removing the contentType check or debugging and seeing what the endpoint is actually returning. Once again, judging by the error you are seeing, seems it's not returning any content type at all. So you should probably be checking that no content type is set.
I know typically the POST request I usually test do not return a contentType at all.
After all, it could be that endpoint is actually doing something incorrectly if you do expect the content type to be set.

Morphia, Embed Mongo and Spring. Address already in use

I am trying use MongoDB, Morphia and Spring and test it, so I started use Embedded Mongo.
When I had only one DAO to persist I did not had any problem with my tests, however, in some cases I needed use more than one DAO, and in that cases my injected Datasore give me an problem: addr already in use.
My Spring Test Database Configuration is this:
#Configuration
public class DatabaseMockConfig {
private static final int PORT = 12345;
private MongodConfigBuilder configBuilder;
private MongodExecutable mongodExecutable;
private MongodProcess mongodProcess;
#Bean
#Scope("prototype")
public MongodExecutable getMongodExecutable() {
return this.mongodExecutable;
}
#Bean
#Scope("prototype")
public MongodProcess mongodProcess() {
return this.mongodProcess;
}
#Bean
public IMongodConfig getMongodConfig() throws UnknownHostException, IOException {
if (this.configBuilder == null) {
configBuilder = new MongodConfigBuilder().version(Version.Main.PRODUCTION).net(new Net(PORT, Network.localhostIsIPv6()));
}
return this.configBuilder.build();
}
#Autowired
#Bean
#Scope("prototype")
public Datastore datastore(IMongodConfig mongodConfig) throws IOException {
MongodStarter starter = MongodStarter.getDefaultInstance();
this.mongodExecutable = starter.prepare(mongodConfig);
this.mongodProcess = mongodExecutable.start();
MongoClient mongoClient = new MongoClient("localhost", PORT);
return new Morphia().createDatastore(mongoClient, "morphia");
}
#Autowired
#Bean
#Scope("prototype")
public EventDAO eventDAO(final Datastore datastore) {
return new EventDAO(datastore);
}
#Autowired
#Bean
#Scope("prototype")
public EditionDAO editionDAO(final Datastore datastore) {
return new EditionDAO(datastore);
}
}
And my DAO classes are similar to that
#Repository
public class EventDAO {
private final BasicDAO<Event, ObjectId> basicDAO;
#Autowired
public EventDAO(final Datastore datastore) {
this.basicDAO = new BasicDAO<>(Event.class, datastore);
}
...
}
My test class is similar to that:
#ContextConfiguration(classes = AppMockConfig.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class EventDAOTest {
#Autowired
private EventDAO eventDAO;
#Autowired
private MongodExecutable mongodExecutable;
#Autowired
private MongodProcess mongodProcess;
#Rule
public ExpectedException expectedEx = ExpectedException.none();
#After
public void tearDown() {
this.mongodProcess.stop();
this.mongodExecutable.stop();
}
...
}
I use prototype scope to solve problem with singleton and make sure that my mock database is clean when I start my test, after that I stop mongod process and mongod executable.
However since I need use more than one DAO I receive that error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'editionDAO' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.mongodb.morphia.Datastore]: :
Error creating bean with name 'datastore' defined in class br.com.mymusicapp.spring.DatabaseMockConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.mongodb.morphia.Datastore]:
Factory method 'datastore' threw exception; nested exception is java.io.IOException: Could not start process: ERROR: listen(): bind() failed errno:98 Address already in use for socket: 0.0.0.0:12345
2015-01-04T01:05:04.128-0200 [initandlisten] ERROR: addr already in use
I know what the error means, I just do not know how can I design my Configuration to solve that. As last option I am considering install a localhost MongoDB just for tests, however I think could be a better solution
That is based on the embedded mongod by flapdoodle, right?
If you want to run multiple tests in parallel (could be changed via JUnit annotations, but it's probably faster in parallel), you cannot use a single, hardcoded port. Instead, let the embedded process select an available port automatically.