Reputation: 2135
I have a class which is dependent on an environment variable. I would like to test it with a mock environment variable.
I think a good structure would be to inject the dependency in Spring Boot and then mock it in the test environment. How do I achieve this?
For example with the following code:
@Component
public class ProcessorClass implements Processor {
public String readFile(String fileName) throws IOException {
String fileOut;
String path = Environment.getVariable("READ_PATH");
fileOut = new String(Files.readAllBytes(Paths.get(path + fileName)));
return fileOut
How would I inject READ_PATH
such that I can then mock it within JUnit5.
Environment
is a fake class that currently returns a value from System.getenv("...")
Thank you.
Upvotes: 2
Views: 999
Reputation: 5443
This can be achieved with SystemStubs - https://github.com/webcompere/system-stubs
There are a few ways to do it. If you know what the environment variable should be set to at coding time, then:
@ExtendWith(SystemStubsExtension.class)
class SomeTest {
@SystemStub
private EnvironmentVariables env = new EnvironmentVariables("READ_PATH", "somepath");
@Test
void someTest() {
// inside here, the variable is set, outside
// it is't
}
}
However, if you want Spring to observe the environment variable too, then you need to use a nested test class for the Spring test. https://github.com/webcompere/system-stubs/blob/main/system-stubs-jupiter/src/test/java/uk/org/webcompere/systemstubs/jupiter/examples/SpringAppWithDynamicPropertiesTest.java has an example
Upvotes: 1
Reputation: 73528
Use a fake instead. It can be a better option when the alternative is something hard to mock. In your case you could of course just define test environment variables, but it can become confusing when the configuration is divided into different places. Not to mention that you might not want (or be able to) read any file in a test environment.
@Component
@Profile("test")
public class ProcessorFake implements Processor {
// Implement without actually reading from a file
}
@Component
@Profile("qa", "prod", "dev")
public class ProcessorClass implements Processor {
// Real class used with other profiles
}
Upvotes: 3
Reputation: 61969
You have not told us what this Environment
is. If there is an Environment.setVariable()
method, use it. If not, keep reading.
Environment.getVariable()
is a static, globally available function. It was created without any consideration whatsoever to the question of testing, and it is not suitable for testing. Therefore, it should not be part of any code which is intended to be tested.
Create a separate component, say EnvironmentWrapper
, whose job is simply to invoke Environment.getVariable()
and return the result; leave that component untested, because it cannot be tested. Then, in your ProcessorClass
class, invoke EnvironmentWrapper
to get the value of the variable. In your test, just mock that class.
Upvotes: 1