简体   繁体   中英

Java - Spring and Mockito - Mocking a constructor parameter that is not a field in class?

I have the following Spring component that I am trying to unit test:

@Component
public class OrderService {

    private final DatabaseConnection dbConnection;
    private final String collectionName;

    @Autowired
    public OrderService(
            DatabaseConnection dbConnection,
            DatabaseConfig databaseConfig) {

        this.dbConnection = dbConnection;
        this.collectionName = databaseConfig.getCollectionName();
    }
    
    //methods etc...
    
    }

The DatabaseConfig class is as follows:

@ConfigurationProperties(prefix = "database")
@ConstructorBinding
public class DatabaseConfig {

    //methods etc...
}

I am trying to inject mocks in my OrderService class as follows:

@RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {

    @InjectMocks
    OrderService orderService;

    @Mock
    DatabaseConnection dbConnection; // working as expected
    @Mock
    DatabaseConfig databaseConfig; // giving null pointer
    @Mock
    DatabaseCollectionConfig databaseCollectionConfig;

    @BeforeEach
    public void setup() {
        MockitoAnnotations.openMocks(this);

        when(databaseConfig.getCollections()).thenReturn(databaseCollectionConfig);
        when(databaseCollectionConfig.getCollectionName()).thenReturn("myCollection");

    }

When I run my test class I get:

org.mockito.exceptions.misusing.InjectMocksException: 
Cannot instantiate @InjectMocks field named 'OrderService' of type 'class com.my.package.OrderService'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null

The issue is that in the OrderService constructor when I debug this line is coming as null:

 this.collectionName = databaseConfig.getCollectionName();

How can I correctly mock databaseConfig.getCollectionName() to solve the null issue?

No need to use @InjectMock annotation because you are using constructor based injection in your service class. Please rewrite your test case like this and try again.



@RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {

    OrderService orderService;

    @Mock
    DatabaseConnection dbConnection; // working as expected
    @Mock
    DatabaseConfig databaseConfig; // giving null pointer
    @Mock
    DatabaseCollectionConfig databaseCollectionConfig;

    @BeforeEach
    public void setup(){
   when(databaseConfig.getCollections()).thenReturn(databaseCollectionConfig);

when(databaseCollectionConfig.getCollectionName()).thenReturn("myCollection");

   orderService = new OrderService(dbConnection, databaseConfig);

       
       

    }
   }

You can try to create a mock for that method and create an object instance instead of using the InjectMocks annotation.

@RunWith(MockitoJUnitRunner.class)
class OrderServiceTest {

    OrderService orderService;

    @Mock
    DatabaseConnection dbConnection; // working as expected
    @Mock
    DatabaseConfig databaseConfig; // giving null pointer
    @Mock
    DatabaseCollectionConfig databaseCollectionConfig;

    @BeforeEach
    public void setup() {
  
        (...)
  
        when(databaseConfig.getCollections()).thenReturn(databaseCollectionConfig);
        when(databaseCollectionConfig.getCollectionName()).thenReturn("myCollection");
        orderService = new OrderService(dbConnection, databaseConfig);

    }

Mock behavior , not values . An @ConfigurationProperties class is just a container for data; it doesn't (generally speaking) do anything. Create a real one with new using your test values (eg, setCollections(testDatabaseCollectionConfig) ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM