I am writing tests in Junit and am using Spring Boot Framework (which I am new to) and need to use different urls to test different environments
Hence, I created 2 resource files in addition to application.properties
1> application-dev.properties
2> application-stage.properties
I created a component which I set the property values to be read into.
Lastly in my test file I am annotating the Test Class with:
#ContextConfiguration(classes = {ComponentName.class})
Also in my application.properties I have this line:
spring.profiles.active=dev
Expected:
When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
Why is the property not getting injected with the value from application-dev.properties?
I've tried several articles from Baeldung (A bit confusing though, the articles demonstrate multiple methods to do the same thing, but does not show 1 full technique to do everything end-to-end
https://www.baeldung.com/spring-profiles
I also tried with setting the active profile multiple ways:
a. Env variable
b. Using #ActiveProfiles annotation
Note: This is a Test Project (I am trying to automate-test a website, so all my resource files are inside src.test.resources
application.properties
#spring
https://ops.dev.website.us
spring.profiles.active=dev
url.sms=https://ops.default.website.us
application-dev.properties
url.sms=https://ops.dev.website.us
application-stage.properties
url.sms=https://ops.stage.website.us
Component File
#Component
#ConfigurationProperties(prefix = "url")
public class DevEnvironment {
private String sms;
public String getSms() {
return sms;
}
public void setSms(String sms) {
this.sms = sms;
}
}
Test File
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
#Autowired
private ConfigurableEnvironment env;
private DevEnvironment devEnvironment;
String url;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
System.out.println("Active profiles:" + env.getActiveProfiles()[0]);
devEnvironment = context.getBean(DevEnvironment.class);
//System.out.println("from set app context:" + devEnvironment.getSms());
url = devEnvironment.getSms();
}
#Test
public void testSms(){
System.out.println("inside test url:" +url);
}
}
Expected: When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
You forgot #SpringBootTest annotation to MyTest class.
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#SpringBootTest //add
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
//
}
I worked fine with this.
reference:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications
Related
I have a Spring Boot version 2.7.0 project with different profiles for dev and test with two different properties files: application-dev.properties and application-test.properties. (I have NO default application.properties file.)
In under test service class I have a property that I want to load its value from application-test.properties file. The service class:
#Service
#RequiredArgsConstructor
public class FileServiceImpl implements FileService {
#Value("${files.root-directory}")
private String fileDirectory;
#Override
#Transactional(readOnly = false)
public File createFile(CreateFileCommand command) {
var filePath = FileUtil.getPath(fileDirectory); // <- fileDirectory is null in tests
// ....
}
}
When I run the application in dev profile, everything is OK. But in tests, fileDirectory is always null.
Test class:
#SpringBootTest
#ActiveProfiles("test")
#ExtendWith(MockitoExtension.class)
public class FileServiceTest {
// ...
}
ApplicationTest class:
#SpringBootTest
#ActiveProfiles("test")
#TestPropertySource("classpath:application-test.properties")
#EnableConfigurationProperties
class ApiApplicationTests {
#Test
void contextLoads() {
}
}
application-dev.properties file:
files.root-directory=${user.home}\\api\\files
application-dev.properties file:
files.root-directory=/home/api/var/api/files
EDIT Screenshot of file structure
Put your properties file under src/test/resources for your (JUnit,Integration, Contract etc) test files. That should work!
Property files under src/main/resources are accessible to source files present under src/main/ only!
I'm trying to create a MockMvc test of a Spring Boot controller. I specifically do not want the entire application context to be spun up, so I am restricting the context to the controller in question. However, the test fails with a 500 with the following log output:
2020-03-03 13:04:06.904 WARN 8207 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class main.endpoints.ResponseDto]
It appears that the Spring Boot context does not know how to find Jackson.
Here is the controller
#RestController
class MyController {
#GetMapping("/endpoint")
fun endpoint(): ResponseDto {
return ResponseDto(data = "Some data")
}
}
data class ResponseDto(val data: String)
The test is as follows:
#SpringBootTest(
classes = [MyController::class],
webEnvironment = SpringBootTest.WebEnvironment.MOCK
)
#AutoConfigureMockMvc
internal class MyControllerTest(#Autowired private val mockMvc: MockMvc) {
#Test
fun `should work`() {
mockMvc.perform(MockMvcRequestBuilders.get("/endpoint").accept(MediaType.APPLICATION_JSON))
.andExpect(
content().json(
"""
{
"data": "Some data"
}
"""
)
)
}
}
The build.gradle file includes the following dependencies:
def jacksonVersion = "2.10.2"
testImplementation("com.fasterxml.jackson.core:jackson-core:2.10.2")
testImplementation("com.fasterxml.jackson.core:jackson-databind:2.10.2")
testImplementation("com.fasterxml.jackson.core:jackson-annotations:2.10.2")
Any ideas on how to get this to work?
The solution is to annotate the class with #WebMvcTest rather than #SpringBootTest. This configures enough context that the test can interact via MockMvc with the controller.
Unfortunately, enabling #WebMvcTest has another side effect: all beans specified by #Bean-annotated methods in the configuration are also instantiated. This is a problem when those methods cannot be executed in a test environment (e.g. because they access certain environment variables).
To solve this, I added the annotation #ActiveProfiles("test") to the test and #Profile("!test") to each such annotated method. This suppresses the invocation of those methods and the test works.
I'm not sure, but I think you need to specify what format the output will be. So something like
#GetMapping(value = ["/endpoint"], produces = [MediaType.APPLICATION_JSON])
So that spring knows to convert it to json and not say XML or something.
#EnableWebMvc might solve your issue.
According to Java Doc:
Adding this annotation to an #Configuration class imports the Spring MVC configuration from WebMvcConfigurationSupport,
e.g.:
#Configuration
#EnableWebMvc
#ComponentScan(basePackageClasses = MyConfiguration.class)
public class MyConfiguration {
}
To customize the imported configuration, implement the interface WebMvcConfigurer and override individual methods, e.g.:
#Configuration
#EnableWebMvc
#ComponentScan(basePackageClasses = MyConfiguration.class)
public class MyConfiguration implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addConverter(new MyConverter());
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyHttpMessageConverter());
}
}
How can I tell Spring to load only application-test.yml and not application.yml file ?
I have a config class:
#ActiveProfiles("test")
#SpringBootConfiguration
public class MongoTestConfig {
#Autowired
MongoOperations operations;
...
}
And a test class:
#RunWith(SpringRunner.class)
#DataMongoTest
#SpringBootTest(classes = MongoTestConfig.class)
public class TagDefinitionRepositoryTest {
...
#Test
....
}
I've tried to add :
#TestPropertySource(locations = {"classpath:application-test.yml"})
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
To my config class but it doesn't work: Spring still load application.yml
I don't think you can tell Spring Boot to ignore application.yml completely. What you can do though is to override all the non desired properties using test specific property files.
Based on the code snippet you posted, any property in application-test.yml will override the equivalent property in application.yml.
Spring Boot considers application-test.yml specific to the profile "test" (which has a higher priority over the default application.yml). No annotation #TestPropertySource is needed.
But if you want to choose another name for your properties file, then you can use #TestProertySource, since files indicated in #TestProperySource parameters have higher priority over the others.
You might want to have a look at Spring Boot external configuration rules for resolving properties
I've end up using #SpringBootTest instead of #DataMongoTest
#SpringBootConfiguration
#ComponentScan(basePackages = {"com.package.services"})
#EnableMongoRepositories(basePackages = {"com.package.repositories"})
public class MongoTestConfig {
private static final MongodStarter starter = MongodStarter.getDefaultInstance();
#Bean
public MongoClient mongoClient() throws IOException {
MongodExecutable _mongodExe;
MongodProcess _mongod;
_mongodExe = starter.prepare(new MongodConfigBuilder()
.version(Version.Main.V3_2)
.net(new Net("localhost", 12345, Network.localhostIsIPv6()))
.build());
_mongod = _mongodExe.start();
MongoClient _mongo = new MongoClient("localhost", 12345);
return _mongo;
}
#Bean
public MongoDbFactory mongoDbFactory() throws IOException{
return new SimpleMongoDbFactory(mongoClient() , "test");
}
#Bean
public MongoTemplate mongoTemplate() throws IOException {
return new MongoTemplate(mongoDbFactory());
}
And my test class is:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyRepositoryTest {
...
I have a #ConfigurationProperties class like this:
#ConfigurationProperties(prefix = "myprops", ignoreUnknownFields = false)
#Configuration
public class MyProperties {
private Long mySchedulerRate;
#Bean
public Long mySchedulerRate() {
return this.mySchedulerRate;
}
}
I'm registering it as a bean so I can refer to it in an annotation for a Spring scheduler:
#Scheduled(fixedRateString = "#{#mySchedulerRate}")
public void runScheduledUpdate() {
...
{
However, I now want to write a unit test where I want to be able to set a different value for the bean 'mySchedulerRate'. Mocking/Spying on the #ConfigurationProperties class doesnt seem to work since the scheduler gets set up before the stubbing has been set to return my desired value.
What is the easiest way to achieve what I am trying to do?
Managed to fix this now. I was running a #SpringBootTest and I realise you can override properties here within the annotation for a particular test class.
This worked for me:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class, properties = "myprops.my-scheduler-rate=1000")
public class MyTest {
So no need to try and override the bean, I was overcomplicating this far too much.
I'm writing integration test for my spring boot application but when I try to override some properties using #TestPropertySource, it's loading the property file defined in the context xml but it's not overriding the properties defined in the annotation.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {DefaultApp.class, MessageITCase.Config.class})
#WebAppConfiguration
#TestPropertySource(properties = {"spring.profiles.active=hornetq", "test.url=http://www.test.com/",
"test.api.key=343krqmekrfdaskfnajk"})
public class MessageITCase {
#Value("${test.url}")
private String testUrl;
#Value("${test.api.key}")
private String testApiKey;
#Test
public void testUrl() throws Exception {
System.out.println("Loaded test url:" + testUrl);
}
#Configuration
#ImportResource("classpath:/META-INF/spring/test-context.xml")
public static class Config {
}
}
I tested this feature with Spring Boot 1.4
Line below works pretty well
#TestPropertySource(properties = { "key=value", "eureka.client.enabled=false" })
Nevertheless new #SpringBootTest annotation is working as well
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "key=value", "eureka.client.enabled=false" }
)
public class NewBootTest {
#Value("${key}")
public String key;
#Test
public void test() {
System.out.println("great " + key);
}
}
I had a similar problem. I fixed it by updating the Spring Context beans.xml to use
org.springframework.context.support.PropertySourcesPlaceholderConfigurer instead of org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.
From the JavaDoc of PropertyPlaceholderConfigurer
Deprecated.
as of 5.2; use org.springframework.context.support.PropertySourcesPlaceholderConfigurer instead which is more flexible through taking advantage of the Environment and PropertySource mechanisms.