I have a bit of code that looks like this:
#Value("#{systemProperties['TARGET_ENVIRONMENT']?: 'qa'}")
private String environment;
In my integration tests, this property is always blank, and I can't seem to do anything to give it definition. I tried doing something like this in my integration tests to simply override the target environment attribute to something else:
#Before
public void setUp(){
System.setProperty("TARGET_ENVIRONMENT", "I love dogs");
}
But that didn't really work out.
#Before
public void setUp(){
System.setProperty("TARGET_ENVIRONMENT", "qa"); -> Not I love Dogs
}
#Value("$peopleSearch{key}") -> Defined as per properties file
private String peopleSearch;
#Value("$peopleSearch{address}") -> Defined as per properties file
private String address;
But that doesn't really change anything. I have other properties (as above) which are being defined in properties files that work out ok and get values, but this one seems to be using the systemProperties attribute, which I have no idea how/where to modify. What do I do to override systemProperties attributes?
systemProperties isn't a function but variable. And it's initialized at the time Spring context is loaded. That's why setting system properties in runtime doesn't effect in your tests. Here you can find more details of SpringEL.
You can pass the system property -DTARGET_ENVIRONMENT to the runner of the test. For example, if you run the test in an IDE (Eclipse or IntelliJ), you can pass it under JVM arguments of the Run Configurations. Or if you run it by Maven, see this.
Related
I am trying to create a unit test, however, whenever I run it I encounter null values on my variables. Debugging the issue points to my environment variables not being used globally on my unit test. As a class I'm testing calls another class that then references its values from an environment variable. How do I make use of global environment variables in IntelliJ?.
I've already added Environment Variables in the Test Configuration like this,
However, this doesn't seem to be used on the entire project. Hence classes calling this environment variables are returning null. How do I make my Unit Test and the classes it calls to use the globally declared environment variables?. TIA. Below is a snippet of my code of how my environment variables are being used in a class
#Value("${fintech.clientid}")
private String clientId;
#Value("${fintech.secret}")
private String clientSecret;
#Value("${digifi-host}")
private String fintechHost;
#Value("${fintech-digifi-login-endpoint}")
private String loginUrl;
#Value("${fintech-single-session-endpoint}")
private String singleSessionUrl;
What this does is from the Class, it calls the values stored in my application.properties file. And from the application.properties file, it tries to look for the proper values in the environment variables declared on run time.
So from Java Class > application.properties file/config > Runtime Environment Variables
Below is the screenshot of variables with null values when debugging the test. As you can see, all of the values are null which means it didn't load the environment variables I have put in the Unit Test. On the other test case where I had a temporary fix (as I put in the answer here), they are populated and hence loading the environment variables properly, but in my many other test cases like this one, it doesn't.
PS:
I've already found related articles in stackoverflow, but they are all test-class specific and that uses surefire plugins or setting the environment variables or via pom, but I don't need it to be there as it is a requirement for us to use environment variables on runtime as the values of the variables should be hidden and not visible on the code. I just simply need the entire project to use a global environment variable when it is doing its Unit Test. Much like how my project would use the environment variables I set in the IDE in normal runtime.
Just for Reference. I already did the ff.:
A.
#ClassRule
public final static EnvironmentVariables environmentVariables = new EnvironmentVariables().set("property1", "value1");
B.
#ContextConfiguration(locations = "classpath:application.properties")
public class LoginServiceTest {
...
}
C.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<environmentVariables>
<SERVER_PORT>8082</SERVER_PORT>
</environmentVariables>
</configuration>
</plugin>
D.
#TestPropertySource(properties = {"property1=value1","property2=value2",})
public class LoginServiceTest {
...
}
E.
public class LoginServiceTest {
static{
System.setProperty("property1", "value1");
System.setProperty("property2", "value2");
...
}
UPDATE:
This solution worked on all of my test. You can refer here for more details.
#RunWith(SpringRunner.class) //Add this
#SpringBootTest(classes = Application.class) //Add this
public class LoginServiceTest {
...
}
Then on
Run > Edit Configurations > Templates (in sidemenu) > JUnit
Then put all your environment variables here.
Click + on the Upper Left window & Apply.
Then delete all existing Unit Test command in your configuration and proceed to re-running all your test again plainly.
Then you'll see the environment variables being added automatically on all your new and upcoming unit test.
You won't have to manually add an environment variables to each unit test command alright as what I happen to do annoying before. Should you ever have an update on your environment variables. Delete the main unit test configuration you need to have an update, and update the JUnit Template instead so that changes will reflect to new unit tests that you may have.
PS:
Thing is, I still encountered the very same issue on some of my Unit
Test, that even though the environment variables are being read
properly on runtime and in some unit test alright with the above configurations. It is only during
the other many Unit Test the very same environment variables are not
being retrieved or received properly by the classes. At first I
thought my classes are not really getting the proper environment
variables and there must be something wrong as to how I stored the
environment variables in IntelliJ, which it did not really, but it
turns out this code format I have is the issue.
#Value("${fintech.clientid}")
private String clientId;
#Value("${fintech.secret}")
private String clientSecret;
#Value("${digifi-host}")
private String digifiHost;
It turns out,even though though this particular code works on runtime, it wont on Unit Test time, like ever.
Hence I tinkered with this particular codes until I was already able to get the proper environment variables on Unit Test. Refer to the changes from the code above to the one that fixed the issue for me below.
#Value("${fintech.clientid}")
public void getClientId(String tempClientId) {
clientId = tempClientId;
}
private String clientId;
#Value("${fintech.secret}")
public void getClientSecret(String tempClientSecret) {
clientSecret=tempClientSecret;
}
private String clientSecret;
#Value("${digifi-host}")
public void getDigifiHost(String tempDigifiHost) {
digifiHost=tempDigifiHost;
}
private String digifiHost;
I work with spring profiles with several configurations (prod, dev, test ...). I'm using several property files such as "application-prod.properties", "application-dev.properties ...). At this point, everything's fine.
But now, I want to add a language management using the same system. I tried to add 2 languages, english and french. So I created 2 more property files : "application-fr.properties" and "application-en.properties". Then I tag main main language configuration class with :
#Profile({ "fr", "en"})
public class MyClass{
public static String MYVAR;
#Value("${myclass.myvar}")
private void setMyVar(String myVar) {
MYVAR = myVar;
}
}
With my config files being like :
myclass.myvar=...
My active dev profile is "dev,en" for example, and it doesn't set my vars.
Any idea how to fix my issue ?
I fixed my issue, my syntax was ok, it was an ordering problem with my class constructions. My config file was initialized too late.
This surely is a common problem. I have a properties file like my-settings.properties which is read by an application class. When I write a test class, it needs to test different scenarios of things that could be present in my-settings.properties in order to ensure maximum code coverage (e.g. empty properties file, basic properties file etc). But I can only have one my-settings.properties in my src/test/resources.
What would be really great is if there was just some annotation
#MockFileOnClassPath(use = "my-settings-basic.properties", insteadOf = "my-settings.properties")
Then I could just have multiple my-settings-XXX.properties files in my /src/test/resources and just annotated the correct one on each test method. But I can't find anything like this. I'm using JUnit 4.12.
I can think of a couple of crude solutions:
Before each test, find the file on the file system, copy it using filesystem I/O, then delete it again after the test. But this is clumsy and involves a lot of redundancy. Not to mention I'm not even sure whether the classpath directory will be writable.
Use a mocking framework to mock getResource. No idea how I would even do that, especially as there are a million different ways to get the file (this.getClass().getResourceAsStream(...), MyClass.class.getResourceAsStream(...), ClassLoader.getSystemClassLoader().getResourceAsStream(...) etc.)
I just think this must be a common problem and maybe there is already a solution in JUnit, Mockito, PowerMock, EasyMock or something like that?
EDIT: Someone has specified that this question is a duplicate of Specifying a custom log4j.properties file for all of JUnit tests run from Eclipse but it isn't. That question is about wanting to have a different properties file between the main and test invocations. For me I want to have a different properties file between a test invocation and another test invocation.
I find that whenever dealing with files, it's best to introduce the concept of a Resource.
eg:
public interface Resource {
String getName();
InputStream getStream();
}
Then you can pass the resource in via dependency injection:
public class MyService {
private final Properties properties;
public class MyService(Resource propFile) {
this.properties = new Properties();
this.properties.load(propFile.getStream());
}
...
}
Then, in your production code you can use a ClasspathResource or maybe a FileResource or URLResource etc but in your tests you could have a StringResource etc.
Note, if you use spring you already have an implenentation of this concept. More details here
You can change your Service class to accept the name of the resource file, then then use that name to load the resource.
public class MyService {
public MyService(String resourceFileName){
//and load it into Properties getResourceAsStream(resourceFileName);
}
}
i have a play 2.3 application that I want to test.
This application has a Global.java class that extends GlobalSettings in order to start a recurring Akka task every 5minutes.
During testing I don't want the task to be scheduled since it creates several issues and I don't need it.
Therefore I would like to override the GlobalSettings.
By reading the documentation, it looks like it should be possible to use a FakeApplication for that. However I tried to do that in several ways and the framework still runs my default global settings.
I created a base class for my tests that looks like this:
public class BaseTest
extends WithApplication
{
protected FakeApplication provideFakeApplication()
{
return fakeApplication(inMemoryDatabase("test"), new GlobalSettings());
}
}
According to the documentation, if a test class extends WithApplication a fake application should automatically start for me, with the configuration provided.
Disregarding if this happens or not, even before the testing methods are called, the default global settings trigger. The "new Global()" doesn't override the default.
I also tried to manually start the fakeApplication using a #BeforeClass annotation, with no success.
I am running the tests with the "activator test" command.
It looks like that the fakeApplication is indeed used for each test, but before even the first test starts, the main application is started and its global triggered. And that's what I don't want it to happen.
Am I doing something wrong or is it a bug in play? if it is a bug, is there a workaround?
EDIT: I just noticed that even the database settings don't get overridden correctly. I normally use a h2 file database for developing, but i want a inmemory, different one for testing. However by using the code above doesn't change the database used, and therefore my tests run against my file DB.
I also tried something like this:
#Test
public void testMyTest()
{
running(fakeApplication(inMemoryDatabase("test2")), () -> {
//TESTING CODE THAT USES DB
});
}
and still any query inside the body runs against the DB configured in the config file, not the inmemory database.
Edit
Chafik solution kind of worked for me, since by specifying a different config file in the build.sbt file I managed to override my settings. Things are still really wierd though:
1)Now if from my fakeApplication constructor I try to override the GlobalSettings by passing a new instance in helper method, the settings are correctly overridden, while before I could not at all override the main one
2)If I revert my change and don't supply a test conf file, I can still override the globalsettings. That is, the behaviour is different than it used to be initially.
Something is definitely strange around the test command, its configuration, running scope, and the way fakeApplication override the configuration, and/or the documentation about it si definitely unclear and lacking. However since at least I achieved what I wanted to do I'll still consider the answer solved.
I did what you want like this.
Set a different config file for testing in your build.sbt
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
Create conf/application.test.conf
Include main configuration file at the beginning include "application.conf"
Override the settings that you want
Create a property like this startAkkaActor=true in the main config file
Create a property like this startAkkaActor=false in the test config file
Update you Global.java where you start your Akka actor
if (Play.application().configuration().getBoolean("startAkkaActor")) {
// Start your Akka actor
}
You can do the same with your database settings
The config file must be defined in build.sbt because Play forks JVM for each test without duplicating the parameters set in the main JVM. The following does not work :
activator test -Dconfig.file=conf/application.test.conf
I wrote a performance test class marked as #Ignore because it takes a very long time to run and I don't normally need to run it. I'd like to be able to right click one of the #Tests and say "run" but when I do that in intellij it doesn't run the test because the class is marked as ignored.
I don't want to comment out the #Ignore because it's too easy to accidentally commit the file with the #Ignore removed. But I feel that if I explicitly tell Intellij to run a test it should run it regardless of the #Ignore. Keep in mind, if I run a group of classes (eg: all in a package) I still would want this test class to be ignored.
Is it possible to get this functionality in Intellij?
I found this link which says that you can do something tricky with Assume.assumeTrue that will mark the test as ignored if the condition is false, and normally this is a system property that is used in the condition, so you can pass it in as a parameter on the command line. IntelliJ lets you customize the command line parameters, so it should work, but I haven't tried it myself.
The example from the link is:
#Test
public void shouldTryEveryPossiblePhoneticAttributeSet() throws IOException {
Assume.assumeTrue(TestEnvironment.hasBigParseSets());
...
}
public class TestEnvironment {
private static final String HAS_BIG_PARSESETS = "hasBigParseSets";
public static boolean hasBigParseSets(){
return "true".equalsIgnoreCase(System.getProperty(HAS_BIG_PARSESETS));
}
}
And "mvn test -P bigParseSets" vs "mvn test".
edit: And I just found this neat thread on StackOverflow that tells how to run a single junit test. I assume I don't need to quote that since it's to a post here on StackOverflow. That explains how to do it from a command line, but you should be able to do something very similar to that one that has hard coded values for the class and method names, and then just right click on the SingleJUnitTestRunner class and ask IntelliJ to run it.