Unable to read system property in a static block - java

I'm facing an issue setting a system property defined in a servlet from the JUnit test. My servlet initialization looks like this:
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID = 4306648703337839989L;
private static final String CLASS_NAME = MyServlet.class.getName();
public static final String PROPERTY_NAME = CLASS_NAME + ".myProperty";
private static final long DEFAULT_PROPERTY_VALUE = 120000;
private static final long PROPERTY_VALUE;
static
{
long propValue = DEFAULT_PROPERTY_VALUE;
final String propertyStr = java.lang.System.getProperty(PROPERTY_NAME);
if (propertyStr != null)
{
propValue = Long.parseLong(propertyStr);
}
PROPERTY_VALUE = propValue;
}
}
Now my JUnit test sets the property value to say 5000 as follows:
#Test
public void testMyServlet()
{
java.lang.System.setProperty(MyServlet.PROPERTY_NAME, "5000");
<test>
}
When I run the test, the value of the static final field 'PROPERTY_VALUE' is resolved to 120000 rather than 5000. So obviously the servlet init happens before the test and that's why the value of the field is already resolved to the default value before the test can set the system property. I'm looking for suggestions to get around this so that my test can set that system property to 5000.
Thanks!
EDIT: Solution
I was able to get the test working by setting the system property to 5000 in two ways:
Solution 1: Set the system property in a static block at the very top of the test class.
public class MyTest
{
static
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
}
#Test
public void testMyServlet()
{
<my test>
}
}
Solution 2: Set the system property in the JUnit test itself but using the property key as the full canonical name.
public class MyTest
{
#Test
public void testMyServlet()
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
<my test>
}
}

I was able to get the test working by setting the system property to 5000 in two ways:
Solution 1: Set the system property in a static block at the very top of the test class.
public class MyTest
{
static
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
}
#Test
public void testMyServlet()
{
<my test>
}
}
Solution 2: Set the system property in the JUnit test itself but using the property key as the full canonical name.
public class MyTest
{
#Test
public void testMyServlet()
{
java.lang.System.setProperty("com.servlets.MyServlet.myProperty", "5000");
<my test>
}
}

Related

Access field in a Rabbitmq testContainer extension class in junit5

I am using TestContainer to run RabbitMQ instance in order to use it in my integration tests.
I create a Junit 5 extension class that implement the BeforeAllCallback interface to run the container only once before my tests, to connect to the container i need to retrieve the mapped port that is exposed in my host machine, so i am wandering if there is any solution in order to access the extension class field from my integration test class.
The Extension
public class RmqExtension implements BeforeAllCallback {
private static final String DEFAULT_USER = "guest";
private static final String DEFAULT_PASS = "guest";
public static final int RABBIT_HTTP_API_PORT = 15672;
private static final String RABBIT_MQ_IMAGE_NAME = "rmqImage";
private static final String RABBIT_MQ_OVERVIEW_PATH = "/api/overview";
private static final GenericContainer rabbitMqContainer = new GenericContainer(DockerImageName.parse(RABBIT_MQ_IMAGE_NAME))
.withExposedPorts(RABBIT_HTTP_API_PORT)
.waitingFor(Wait.forHttp(RABBIT_MQ_OVERVIEW_PATH).withBasicCredentials(DEFAULT_USER, DEFAULT_PASS).forStatusCode(HttpStatus.SC_OK));
#Override
public void beforeAll(ExtensionContext extensionContext) throws Exception {
rabbitMqContainer.start();
}
}
My test Class
#ExtendWith(RmqExtension.class)
class RabbitMqIT {
private int myPort;
#Test
void myTest(){
// What i need to do
myPort = rabbitMqContainer.getMappedPort(15672);
}
}
I am unsure what is the most elegant JUnit-Jupiter-idiomatic way to do this, but if there is only 1 instance of the container per JVM process, you could either use a public static field or save it System Properties.
Also, see the Singleton Container Pattern for another example of how to do this without JUnit:
https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers

How to print out environment variables, when starting a Micronaut application

I am working on a Micronaut project, where I would like to see if the environment variables from the application.yml are being correctly assigned using the #Value annotation, when the app starts locally.
But every time the app is starting it shows me that the variables are not being assigned to the environment variables from the application.yml file.
That is my code:
public class Application {
private static String localTestString = "I am the local String";
#Value("${aws.secretkeyid}")
public static String applicationYmlTestString;
#Value("${aws.keyid}")
private static int keyId;
public static void main(String[] args) {
Micronaut.run(Application.class);
}
static{
log.warn("Local Test String is: " + localTestString);
log.warn("Application Yml Test String is: " + applicationYmlTestString);
log.warn("Key ID: " + keyId);
}
}
This is my application.yml
aws:
keyid: 123
secretkeyid: "abcdesdasdsddddd"
region: "europe-1"
Output:
Local Test String is: I am the local String
Application Yml Test String is: null
Key ID: 0
As we see the two variables applicationYmlTestString and keyId are not being assigned to the environment variables. Is there a way to solve this problem and to get:
Application Yml Test String is: abcdesdasdsddddd
Key ID: 123
Thank you in advance!
There are two issues with the example you have shown. Firstly, Micronaut does not inject values to static fields annotated with #Value annotation. (It's not weird, Spring does not support it as well.) Secondly, after injecting values to non-static fields, you won't be able to read their values using the class' static constructor. The whole application context must be ready to read such values, so you need to use an event listener that reacts to the application startup event.
Here is the simplest way to achieve it based on your example:
package micronaut.hello.world;
import io.micronaut.context.annotation.Value;
import io.micronaut.context.event.StartupEvent;
import io.micronaut.runtime.Micronaut;
import io.micronaut.runtime.event.annotation.EventListener;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Singleton
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
#Value("${aws.secretkeyid}")
private String applicationYmlTestString;
#Value("${aws.keyid}")
private int keyId;
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
#EventListener
void onStartup(StartupEvent event) {
log.warn("Application Yml Test String is: " + applicationYmlTestString);
log.warn("Key ID: " + keyId);
}
public String getApplicationYmlTestString() {
return applicationYmlTestString;
}
public void setApplicationYmlTestString(String applicationYmlTestString) {
this.applicationYmlTestString = applicationYmlTestString;
}
public int getKeyId() {
return keyId;
}
public void setKeyId(int keyId) {
this.keyId = keyId;
}
}
There are three things worth mentioning:
The above example uses #EventListener annotation that makes the given method "event-aware", and this method will be triggered when the specific event is published by the application (or framework.)
We react to io.micronaut.context.event.StartupEvent - an event fired once startup is complete.
Keep in mind that to make this #EventListener annotation work, we need to annotate the application class with #Singleton to make this class a proper Micronaut bean.
Alternatively, if making an application class a singleton bean does not look good to you, you can implement the ApplicationEventListener interface and create a dedicated bean that will react to the same startup event. In this example, I use a static inner class, but that's just to make this example simple:
package micronaut.hello.world;
import io.micronaut.context.annotation.Value;
import io.micronaut.context.event.ApplicationEventListener;
import io.micronaut.context.event.StartupEvent;
import io.micronaut.runtime.Micronaut;
import jakarta.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
#Singleton
static class OnStartupEventListener implements ApplicationEventListener<StartupEvent> {
#Value("${aws.secretkeyid}")
private String applicationYmlTestString;
#Value("${aws.keyid}")
private int keyId;
#Override
public void onApplicationEvent(StartupEvent event) {
log.warn("Application Yml Test String is: " + applicationYmlTestString);
log.warn("Key ID: " + keyId);
}
public String getApplicationYmlTestString() {
return applicationYmlTestString;
}
public void setApplicationYmlTestString(String applicationYmlTestString) {
this.applicationYmlTestString = applicationYmlTestString;
}
public int getKeyId() {
return keyId;
}
public void setKeyId(int keyId) {
this.keyId = keyId;
}
}
}
But eventually, you should consider implementing a configuration class and use it instead of injecting values with the #Value annotation. However, whatever option you choose, the same thing applies - the configuration class can be injected to a non-static field and can be checked using an event listener mechanism.
And as Tim mentioned in the comment below, "Be careful logging environment variables though... They have a habit of being secrets, and logging them out tends to end up with them being in plain text in loads of different systems 😉". If you really need to log such information to double-check if the expected configuration is injected, try doing it in the controlled dev environment only. Assuming that you use the dev profile for the local env, you could use #Requires annotation to limit specific event listener to only that dev environment:
#Singleton
#Requires(env = "dev")
class OnStartupEventListener implements ApplicationEventListener<StartupEvent> {
#Value("${aws.secretkeyid}")
private String applicationYmlTestString;
#Value("${aws.keyid}")
private int keyId;
#Override
public void onApplicationEvent(StartupEvent event) {
log.warn("Application Yml Test String is: " + applicationYmlTestString);
log.warn("Key ID: " + keyId);
}
public String getApplicationYmlTestString() {
return applicationYmlTestString;
}
public void setApplicationYmlTestString(String applicationYmlTestString) {
this.applicationYmlTestString = applicationYmlTestString;
}
public int getKeyId() {
return keyId;
}
public void setKeyId(int keyId) {
this.keyId = keyId;
}
}

Where does Spring stores strings loaded from *.properties files?

I have this two files:
application.properties
// ... stuffs ...
spring.profiles.active=some_id
application-some_id.properties
// ... stuffs ...
some.string=value
I want to get the value of the property "some.string" from my java code like this:
Utility.java
public final class Utility {
public final static String SOME_STRING_VALUE = <Something>.getProperty("some.string");
private Utility() {
}
}
What should I write instead of <Something>? I know that working with spring I should use spring stuffs like #Value, #Component, etc ... and 99% of the project is like that. I'd like to have just this exception.
Try using the Environment API :
#Autowired
private Environment env;
.
.
env.getProperty("some.string");
Update :
To be able to use in a static context you can try using lazy instantiation like follow (but in this case you should remove final accessor) :
final class Utility {
#Autowired
private Environment env;
private static String SOME_STRING_VALUE;
public static String getStringValue() {
if (SOME_STRING_VALUE == null) {
SOME_STRING_VALUE = env.getProperty("some.string");
}
return SOME_STRING_VALUE;
}
private Utility() {}
}
Hope this helps

Mock private method with new object creation

I've written a class which reads the entire file and returns the content.
class ClassToTest {
public methodToTest(String input) {
return privateMethod(input);
}
private privateMethod(input) {
ClassPathResource classPathResource = new ClassPathResource(input);
IOUtils.toString(classPathResource.getFile());
}
}
Now, inside my test class, I don't want my test to actually read the file from so I'm trying to mock the method classPathResource.getFile() but somehow I'm not able to do so without writing PrepareForTests() and if I do that those test are not counted in JaCoCo.
I've written test case as
#Test
public void test_methodToTest() {
mockStatic(IOUtils.class);
when(IOUtils.toString(any()).thenReturn("DUMMY_STRING");
methodToTesT("file1.txt");
...
}
The problem is IOUtils.toString gets mocked properly but the call classPathResource.getFile() tries to access the file on the disk. For this, I can do this
PowerMockito.whenNew(ClassPathResource.class)
.withAnyArguments().thenReturn(mockedClassPath);
And add annotation to my test class as
#PrepareForTest(ClassToTest.class)
class MyTestClass {
...
}
But now the problem is this test class is skipped from the JACOCO test coverage . How can I write tests for this class?
You can pass a mocked reference into the constructor doing this:
class ClassToTest {
private ClassPathResource classPathResource;
public ClassToTest(ClassPathResource classPathResource) {
this.classPathResource = classPathResource;
}
public methodToTest(String input) {
IOUtils.toString(classPathResource.getFile(input));
}
}
Or you can pass the mocked reference into the method doing this:
class ClassToTest {
public methodToTest(ClassPathResource classPathResource) {
IOUtils.toString(classPathResource.getFile());
}
}
Having to mock a private member should be seen as a code smell and an indication that something is wrong with the current design. Because ClassPathResource is being initialized internal to the subject class it is now tightly coupled to that class. While not entirely impossible to mock it does make testing the class cleanly more difficult. Consider inverting the creation of the class to a delegate as a dependency.
public interface PathResource {
String getFile(String input);
}
This will allow the injection of the dependency
class ClassToTest {
private classPathResource;
public ClassToTest (PathResource resource) {
this.classPathResource = resource;
}
public String methodToTest(String input) {
return privateMethod(input);
}
private String privateMethod(String input) {
return IOUtils.toString(classPathResource.getFile(input));
}
}
and the dependency can be mocked/faked/stubbed when testing.
public void Test() {
//Arrange
//mock creation
PathResource resource = mock(PathResource.class);
String input = "path";
String expected = "expected_output";
//stubbing
when(resource.getFile(input)).thenReturn(expected);
ClassToTest subject = new ClassToTest(resource);
//Act
String actual = subject.methodToTest(input);
//Assert
verify(resource).getFile(input);
assertEquals(expected, actual);
}
in production code the ClassPathResource would be derived from the abstraction
public class ClassPathResource implements PathResource {
//...code removed for brevity
}
and it would be associated with the abstraction at the composition root.
Following the above suggestions would now allow ClassToTest to be tested in isolation without any knock on effects of implementation concerns.

Singleton returning new instance when accessed from test method

I am using Junit 4.12 with PowerMock 1.6 with Mockito. I have also used PowerMockRule library as described here. I am trying to execute initialization code for all of my test cases exactly once as described in this SO Thread. Its executing the initialization code exactly one time however, if I do ServiceInitializer.INSTANCE inside test method it returns me new object. I am not able to understand this behavior. Does anyone have any idea why this is happening? If I execute my code without PowerMockRule Library and run my test with PowerMockRunner then it works fine but in that case my ClassRule is not getting executed.
public class ServiceInitializer extends ExternalResource {
public static final TestRule INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
#Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this); //Print Address #3702c2f1
}
#Override protected void after() {
}
}
class BaseTest{
#Rule
public PowerMockRule powerMockRule = new PowerMockRule();
#ClassRule
public static final TestRule serviceInitializer = ServiceInitializer.INSTANCE;
#Before
public final void preTest() {
// some code
}
#After
public final void postTest() {
//some code
}
}
#PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
#Test
public void testMethodA_1(){
System.out.println(ServiceInitializer.INSTANCE);//Print Address #54d41c2b
}
}
Update
I printed the classloader for the classes and it turns out for first print statement the classloder was sun.misc.Launcher$AppClassLoader and for the second print statement the classloder was org.powermock.core.classloader.MockClassLoader. How can I solve this?
You don't have a singleton. You have a static INSTANCE variable. Keep in mind that one of those can exist for every classloader you have.
Instead make an enum of ServiceInitializer, like so
public enum ServiceInitializer {
INSTANCE;
// rest of class goes here
}
And rely on the JVM's language contracts to ensure the singleton.
Or, better yet, write your code to handle situations where more than one ServiceInitializer can exist, but it just happens that your program only uses one instance. This is the ideal choice, allowing you to alternate between the real ServiceInitializer and a mock if desired.
Edwin is correct; this is an issue with PowerMock creating a new ClassLoader for every test. I strongly recommend refactoring your code so it can be tested without PoeerMock and switch to Mockito.
These books may be helpful
Working Effectively With Legacy Code
Refactoring to Patterns
In the mean time, you can reference ServiceInitializer from your base class:
public class ServiceInitializer extends ExternalResource {
public static final ServiceInitializer INSTANCE = new ServiceInitializer();
private final AtomicBoolean started = new AtomicBoolean();
#Override protected void before() throws Throwable {
if (!started.compareAndSet(false, true)) {
return;
}
// Initialization code goes here
System.out.println("ServiceInitializationHelper:"+this);
}
#Override protected void after() {
}
}
class BaseTest{
#Rule
public PowerMockRule powerMockRule = new PowerMockRule();
#ClassRule
public static final ServiceInitializer serviceInitializer = ServiceInitializer.INSTANCE;
#Before
public final void preTest() {
// some code
}
#After
public final void postTest() {
//some code
}
}
#PrepareForTest({MyClass.class})
public class MyTest extends BaseTest {
#Test
public void testMethodA_1(){
System.out.println(serviceInitializer);
}
}
Well I finally found the work around for this problem. As explained in my question my class was getting loaded by two different class loaders and thus causing problems for me. In order to resolve my issue I used #PowerMockIgnore annotation in order to defer its loading as follows:
#PowerMockIgnore({"com.mypackage.*"})
class BaseTest{
// Stuff goes here
}
This annotation tells PowerMock to defer the loading of classes with the names supplied to value() to the system classloader. You can read about this annotation from here.

Categories

Resources