How to inject @Value constructor parameter using Mockito annotation

Viewed 875

I have a class:

public class MyClass{
    MyClass(
            @Value("${my.protocol}") String protocol,
            @Value("${my.host}") String host,
            @Value("${my.port}") int port,
            @Autowired MyService myservice) {
        ........
    }

    ....

}

I then wrote a test that uses Mockito:

@ExtendWith(MockitoExtension.class)
class MyClassTest {

    @Mock
    private MyService myservice;

    @InjectMocks
    private MyClass myClass;

    ....
}

The test failed with this:

org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'myClass'! Cause: the type 'MyClass ' has no default constructor
You haven't provided the instance at field declaration so I tried to construct the instance.
Examples of correct usage of @InjectMocks:
   @InjectMocks Service service = new Service();
   @InjectMocks Service service;
   //and... don't forget about some @Mocks for injection :)

I think this is because I only provided one of the 4 construction parameters, not the other three which has @Value annotation.

Can someone please let me know how I can inject the three @Value construction parameters in order for this to work?

I use Junit 5 and Mockito.

2 Answers

Annotation-based magic is showy, but it's not the solution to every problem. Just do things the old-fashioned way:

@BeforeEach
void setup() {
  this.subject = new MyClass("http", "localhost", 5000, mockService);
}

Mockito uses reflection inorder to initialize your instances so there will be no injection happening at the initialization step, it'll simply get the constructor and issue #invoke() method on it.

What you should do in this case is mock the values instead of mocking the whole container, the container here is MyClass.

I'm assuming you are using a .yml or a .properties file to assign the values, by creating a new application-${env}.yml this way you'll have your test values separately from the prod environment values.

Or if you have different values for each case, then before each replace the MyClass instance that's in the context with the one you modify:

@BeforeEach
void beforeEach() {
MyClass bean =// get the context and get a reference of `MyClass` bean
bean = new MyClass(...);
}
Related