4

I am writing a junit test cases for one of component in spring boot application. That component is having @Value annotation and reading value from property file.

When I am running my Junit 5 (mockito) and controls goes to the component; the value is null.

What I have tried: I used @ExtendWith(SpringRunner) and changed @injectMocks to @Autowired and @mock to @MockBeans, but that is not I want (As its became integration test.)

Junit class:

@ExtendWith(MockitoExtension.class)
public class ItemMessageProcessorTest {

    private static final String VALUE1 = "Value 1";
    private static final String VALUE2 = "Value 2";
    private static final String VALUE3 = "Value 3";

    @InjectMocks
    private MyComponent component;

    

Component class:

@Slf4j
@Component
public class MyComponent {

    @Value("${my-val.second-val.final-val}")
    private String myValue;

This myValue is being used here in the same component class:

 public void myMethod(){
    myObject.setMyValue(Integer.parseInt(myValue));
}

What I was looking for is something like: If I can by any chance mock the parseInt, or load the values from test class itself. Any lead would be a great help. Note: I can't change anything in the component class.

DeskToDevelop
  • 439
  • 1
  • 5
  • 16
  • Do you have `application.properties`/`application.yml` in your test directory? If you have - you should put property of `@Value` you need there – Alex Oct 21 '21 at 06:26
  • You could simply switch to constructor injection… – slauth Oct 21 '21 at 06:27
  • @Alex those config files are only processed by Spring in integration tests, not standalone unit tests. – slauth Oct 21 '21 at 06:29
  • I don't think this file type "yaml" or "properties" is playing any role here. @Alex Anyway its "yaml" – DeskToDevelop Oct 21 '21 at 06:53

4 Answers4

2

You can just use Spring reflection utills method for setting the field value with @Value for unit test:

org.springframework.test.util.ReflectionTestUtils.setField(classUnderTest, "field", "value");
Dmitrii B
  • 2,672
  • 3
  • 5
  • 14
  • 2
    Perfect answer. This is what I just tried and it worked. – DeskToDevelop Oct 21 '21 at 06:43
  • This is a good answer but my problem is that i have 6 fields in application.properties and they are changeable so how to read them from there if possible without turning this in integration test? – Levijatanu Apr 27 '22 at 15:35
1

I would go for constructor injection in this case:

@Slf4j
@Component
public class MyComponent {

    private final String myValue;

    MyComponent(@Value("${my-val.second-val.final-val}" String myValue)) {
        this.myValue = myValue;
    }
}
slauth
  • 2,667
  • 1
  • 10
  • 18
1

Values from application.properties are loaded into Spring Application Context in these cases:

  • when application is running,
  • when Spring Integration tests are runnign.

In case of Unit tests properties are not loaded.

If you'd have a constructor injection you could set a value for tests and pass it to the constructor.

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
GreenCave
  • 11
  • 2
0

Most important thing, you don't want to load the entire Spring Application context to bind the properties automatically as this would hugely slow down the execution of the unit tests. Therefore, you cannot use @ExtendWith(SpringRunner).

You can make use of ReflectionTestUtils along with @ExtendWith(MockitoExtension.class) to bind the properties of MyComponent class.

org.springframework.test.util.ReflectionTestUtils.setField(MyComponent.class, "myValue", 1);

Valeria
  • 11
  • 1
  • This is basically the already accepted answer with the added hint of not loading the entire application context. – Pao Sep 20 '22 at 09:11