how to pre-populate spring properties from tests - spring

I've a slight race condition when it comes to loading spring properties for an integration test using #TestPropertySource.
Consider the following;
test (using Spock but same for JUnit)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(locations = "classpath:test/simple-test.properties")
class SimpleStuff extends Specification {
public static final String inputDirectoryLocation = "/tmp/input-test-folder"
def "test method"() {
//do test stuff
}
}
simple-test.properties
inputDirectoryLocation=/tmp/input-test-folder
Spring Component
#Component
class SpringComponent {
#Value('${inputDirectoryLocation}')
String inputDirectory;
//do other stuff
}
The above works fine but how would I make the test fully isolated and NOT have a dependency on the FileSystem having the folder /tmp/input-test-folder (as not all users running this test are allowed to create a /tmp folder on their FS)
For example, I would like to use something like
inputDirectoryLocation = Files.createTempDirectory()
so that
#Value('${inputDirectoryLocation}')
String inputDirectory;//equals the output of Files.createTempDirectory()
resulting in test using the OS default temporary folder location & allows us to have the test simply delete the temp folder on cleanup. Is there an eloquent solution to solve the above?
Note: using Spring boot 1.5

Turned out simple enough - simply had to change the value in the properties file to refer to the
inputDirectoryLocation=${java.io.tmpdir}/input-test-folder
Then have my Spock specification create the temp folder prior to launching Spring (by using the setup() fixture method )

Related

Load custom properties file in Spring Boot MVC Main

I have created a myApp.properties in resources folder location and mentioned the server.port in this file.
myApp.properties
myApp.server.port=8020
Now I want to read load this property into my application. But I have to read this before I actually a server.
Here I am trying to do like this
#SpringBootApplication
#ComponentScan(basePackages = {"com.myorg.myapp" })
#EnableConfigurationProperties
#PropertySource("classpath:myApp.properties")
#Component
public class MyAppApplication {
#Value("${myApp.server.port}")
private static String serverPort;
public static void main(String[] args) throws Exception{
try {
SpringApplication appCtxt = new SpringApplication(MyAppApplication.class);
appCtxt.setDefaultProperties(Collections
.singletonMap("server.port", serverPort));
appCtxt.run(args);
} catch (Exception e) {
e.printStackTrace();
}
}
But serverPort is coming as null.
I also tried to create a separate Config file like this but it can't be accessed in static main
#Configuration
#PropertySource("myApp.properties")
#ConfigurationProperties
public class MyAppConfig {
#Value("${myApp.server.port}")
private String serverPort;
/**
* #return the serverPort
*/
public String getServerPort() {
return serverPort;
}
}
Any suggestion would be helpful.
Spring boot injects properties during the initialization of the application context.
This happens (gets triggered) in the line:
appCtxt.run(args);
But you try to access the property before this line - that why it doesn't work.
So bottom line, using "#Value" in the main method doesn't work and it shouldn't.
Now from the code snippet, it looks like you could merely follow the "standards" of spring boot and create the file application.properties with:
server.port=1234
The process of starting the embedded web server in spring boot honors this property and bottom line it will have the same effect and Tomcat will be started on port 1234
Update 1
Based on OP's comment:
So, how can I have multiple application.properties.
In the Spring Boot's documentation it is written that application.properties are resolved from the classpath. So you can try the following assuming you have different modules A,B,C and web app D:
Create src/main/resources/application.properties inside each of 4 modules and pack everything together. The configuration values will be merged (hopefully they won't clash)
If you insist on naming properties A.properties, B.properties and C.properties for each of non-web modules, you can do the following (I'll show for module A, but B and C can do the same).
#Configuration
#PropertySource("classpath:A.properties")
public class AConfiguration {
}
Create in Module A: src/main/resources/A.properties
If you need to load the AConfiguration automatically - make the module A starter (using autoconfig feature of spring-boot):
Create src/resources/META-INF/spring.factories file with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
<package_of_AConfiguration>.AConfiguration
Also this has been the requirement to separate C from entire bundle where it might run as bundle for some and as a separate for some others
Although I haven't totally understood the requirement, but you can use #ConditionalOnProperty for configuration CConfiguration (that will be created just like AConfiguration.java in my previous example) but this times for module C.
If the conditional is met, configuration will run and load some beans / load its own properties or whatever. All in all conditionals (and in particular Profiles in spring) can help to reach the desired flexibility.
By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.
If you save myApp.server.port=8020 in application.properties, it will work fine.
To register a custome property file, you can annotate a #Configuration class with the additional #PropertySource annotation:
#Configuration
#PropertySource("classpath:custom.properties")
#PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}
make sure, your class path is correct.

How to use the PropertySource file for unit tests?

I use Spring in my application, and I want to create a unit test with JUnit. How to run a test with the “production” properties file? How to run a test with a test property file?
This is a data class describing a property file:
#Component
#ConfigurationProperties(prefix = "time")
#PropertySource(
value = ["classpath:config/targets/target.time.config.yml"],
factory = YamlPropertySourceFactory::class,
encoding = "UTF-8"
)
data class TargetTimeUnitConfig( ... )
This is a test in which I want to test my DateTimeParser with production or specific properties from TargetTimeUnitConfig:
#SpringBootTest(classess = [TargetTimeUnitConfig::class, DateTimeParser::class]
#ExtendWith(SpringExtension::class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DateTimeParserTest {
I want DateTimeParserTest to run only with TargetTimeUnitConfig, DateTimeParser in the Spring Context.
Now, of course, the file from the specified directory (classpath:config/targets/target.time.config.yml) is not loaded.
How to fix it? How to load properties from test resources?
It's my solution:
TargetTimeUnitConfig
#Component
#ConfigurationProperties(prefix = "time")
#PropertySource(
value = ["\${spring.config.location:classpath:}\${targetTimeConfig:config/targets/target.time.config.yml}"],
factory = YamlPropertySourceFactory::class,
encoding = "UTF-8"
)
data class TargetTimeUnitConfig(
TargetTimeUnitConfigTest
#SpringBootTest(
classes = [DateTimeParser::class],
properties = ["targetTimeConfig=targettimeunitconfig/target.time.config.yml"])
#ExtendWith(SpringExtension::class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#EnableConfigurationProperties(TargetTimeUnitConfig::class)
class TargetTimeUnitConfigTest {
I use 2 environment variables (spring.config.location, targetTimeConfig), which allow running tests and applications with different configs. It also allows you to change the location of production configs.
But it looks scary, and contains many places for potential errors.

Unit testing mapstruct with 'uses'

Is there a standard approach for unit testing mappers when using spring boot? i.e. getting dependencies etc.
I'm currently doing something like this:
#SpringBootTest(classes = {VehicleMapper.class, VehicleMapperImpl.class,
VehicleAttributesMapper.class, VehicleAttributesMapperImpl.class})
#RunWith(SpringRunner.class)
public class VehicleMapperTest {
#Autowired
private VehicleMapper vehicleMapper;
VehicleMapper has a uses:
#Mapper(componentModel = "spring", uses = VehicleAttributesMapper.class)
However when I run my unit test the VehicleAttributesMapper does not appear to get invoked? (the types match i.e. source/target)
TIA
Well it turned out that I needed to add an explicit mapping in the VehicleMapper:
#Mapping(source = "vehicleInfo", target = "vehicleAttributes")
I had thought that this would not need to be done explicitly. In the docs under "Invoking other mappers" it seems to suggest you don't (in the example anyway).

Spring profiles on integration tests class

we have selenium tests which are ran by java test class.
On local environment everything is ok, but I want to switch off those tests when run on jenkins.
So I use:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebIntegrationTest("server.port=1234")
#Profile("!jenkins")
#ActiveProfiles("integrationtests")
public class LoginAndEditProfileSeleniumTest {
...
What works:
running mvn clean test run all tests locally, with integrationtests profile active. I dont want to pass any additional parameter.
What I want to achieve:
running mvn clean test -Dspring.profiles.active=jenkins switch off this test.
Can I merge somehow profile passed by parameter, ActiveProfile annotation and take Profile annotation into consideration? :)
//update:
Its possible to use class extending ActiveProfilesResolver:
public class ActiveProfileResolver implements ActiveProfilesResolver {
#Override
public String[] resolve(Class<?> testClass) {
final String profileFromConsole = System.getProperty("spring.profiles.active");
List<String> activeProfiles = new ArrayList<>();
activeProfiles.add("integrationtests");
if("jenkins".contains(profileFromConsole)){
activeProfiles.add("jenkins");
}
return activeProfiles.toArray(new String[activeProfiles.size()]);
}
}
but it seems to not to cooperate with #Profile anyway ( jenkins profile is active but test is still running ) .
#Profile has zero affect on test classes. Thus, you should simply remove that annotation.
If you want to enable a test class only if a given system property is present with a specific value, you could use #IfProfileValue.
However, in your scenario, you want to disable a test class if a given system property is present with a specific value (i.e., if spring.profiles.active contains jenkins).
Instead of implementing a custom ActiveProfileResolver, a more elegant solution would be to use a JUnit assumption to cause the entire test class to be ignored if the assumption fails.
This should work nicely for you:
import static org.junit.Assume.*;
// ...
#BeforeClass
public static void disableTestsOnCiServer() {
String profilesFromConsole = System.getProperty("spring.profiles.active", "");
assumeFalse(profilesFromConsole.contains("jenkins"));
}
Regards,
Sam (author of the Spring TestContext Framework)

How to priortized the test cases in maven run?

I have 3 test cases i.e. 1 2 3. How will i give priority as 2 1 3 while executing maven command.
I assume you want to do it because you need some prerequisition prior the test can be run. You can do it by #Before Annotation prior the actual testcase, or you can call other tests from the test method.
Say, testClient() test will test and verify that new client can be added to the system. Then you can do this:
#Test
public void testWithdrawal(){
testClient(); // i need client existing before the test can be run
// ... do something else
}
In that case you have assured that prerequisites are fullfilled and dont have to worry much about the testcases order
EDIT
I think I understand your needs, because I am in quite similar situation. How I solved it:
For create I have special class, which can create me a data and return needed data. So, i have something like:
#Test
public void testShare(){
CreateTests create = new CreateTests; //This will just initialize the object
create.testCreate(); // this method can contain steps needed to create
String justCreatedEntity = create.getEntity(); // just example how you can use the just created entity in further tests
}
And my class to solve the create is something like this
public class CreateTests{
private static String entity; //static because i dont want it to be flushed when test ends
public void testCreate() throws Exception{
WebDriverBackedSelenium selenium = new WebDriverBackedSelenium(driver, "baseURL");
selenium.... // All the selenium stuff
setEntity(selenium.getText("id=mainForm.createdentity"));
}
public void setEntity(String ent){
this.entity = ent;
}
public String getEntity(){
return entity;
}
Its just an outline - but basically, I have these "crucial" entities as standalone objects, called by the test class. Inside test, i verify everything throgh getters. Like:
Assert.assertNotNull(create.getAuctionID(),"New Entity is NULL!" );
You can run mvn test with options to specify a single test, or multiple tests. The order they are run in is the order specified on the command line.
Reference is here: http://maven.apache.org/plugins/maven-surefire-plugin/examples/single-test.html
Note that Java suggests that the good unit testing practice of tests not requiring to be run in order and test to not rely on each other:
http://java.sun.com/developer/Books/javaprogramming/ant/ant_chap04.pdf

Resources