I have a class that Autowires another class with #ConfigurationProperties.
Class with #ConfigurationProperties
#ConfigurationProperties(prefix = "report")
public class SomeProperties {
private String property1;
private String property2;
...
Class that Autowires above class SomeProperties
#Service
#Transactional
public class SomeService {
....
#Autowired
private SomeProperties someProperties;
.... // There are other things
Now, I want to test SomeService class and in my test class when I mock SomeProperties class, I am getting null value for all the properties.
Test class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SomeProperties.class)
#ActiveProfiles("test")
#EnableConfigurationProperties
public class SomeServiceTest {
#InjectMocks
private SomeService someService;
#Mock // I tried #MockBean as well, it did not work
private SomeProperties someProperties;
How can I mock SomeProperties having properties from application-test.properties file.
You are not mocking SomeProperties if you intend to bind values from a properties file, in which case an actual instance of SomeProperties would be provided.
Mock:
#RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
#InjectMocks
private SomeService someService;
#Mock
private SomeProperties someProperties;
#Test
public void foo() {
// you need to provide a return behavior whenever someProperties methods/props are invoked in someService
when(someProperties.getProperty1()).thenReturn(...)
}
No Mock (someProperties is an real object that binds its values from some propertysource):
#RunWith(SpringRunner.class)
#EnableConfigurationProperties(SomeConfig.class)
#TestPropertySource("classpath:application-test.properties")
public class SomeServiceTest {
private SomeService someService;
#Autowired
private SomeProperties someProperties;
#Before
public void setup() {
someService = new someService(someProperties); // Constructor Injection
}
...
You need to stub all the property values in #Test/#Before methods of SomeServiceTest.java like :
ReflectionTestUtils.setField(someProperties, "property1", "value1");
ReflectionTestUtils.setField(object, name, value);
You can also mock the reponse for dependent classes via Mockito.when()
If you are using #Mock , you need to stub the values as well. By default the value will be null for all the properties.
Related
I have a class that autowires another #ConfigurationProperties class.
#Configuration
#Data
#ConfigurationProperties("api")
public class ApiConfiguration {
private String myUrl;
}
....
Class that autowires above class ApiConfiguration
public class Services implements anotherServices {
#Autowired
private ApiConfiguration apiConfiguration;
....
#Override
public Mono<SomeClass> getClassInfo(String ..., String ...) {
return someWebClient
.get()
.uri(apiConfiguration.getRewardsBalance()) ---> **Null pointer**
Now when I autowire ApiConfiguration class, if I System.out the output I see that it's been set with whatever String I passed in, meaning My junit class is autowiring apiConfiguration class however, the actual implementation class throws a null pointer exception on apiConfiguration.
Test class
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
#EnableConfigurationProperties(value = {ApplicationConfiguration.class,ApiConfiguration.class})
#TestPropertySource("classpath:application.properties")
#ContextConfiguration(
initializers = ApplicationContextInitializer.class)
class AccountDetailsServicesTest {
#Autowired
private ApiConfiguration apiConfiguration;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
...
apiConfiguration.setApi("http://localhost:8080");
...
Is there a way to initialise mocks before they are injected into another component?
To give an example I have following classes:
#Service
SomeService {
#Autowired
public SomeService(SomeConfig config)
}
#Configuration
#Getter
#Setter
public class SomeConfig {
private String someValue;
}
In my test I'm doing the following:
#MockBean
SomeConfig someConfig;
#Autowired
SomeService someService;
The problem is, that the SomeService constructor is already accessing SomeConfig members, before I can even initialise it with when(someConfig.getSomeValue()).thenReturn("something"), resulting in a NullPtrException.
Is there a hook that gets executed before SomeService is instantiated?
You could manually setup your Service in a setup method.
Just make sure to exclude your SomeService from classpath scanning in those tests.
SomeServiceTest {
#MockBean
SomeConfig someConfig;
SomeService someService;
#BeforeEach
public void setup(){
// init mocks
// setup other stuff
someService = new SomeService(someConfig);
}
}
I assume that the SomeService looks like:
#Service
SomeService {
private final SomeConfig config
#Autowired
public SomeService(SomeConfig config) {
this.someConfig = someConfig;
// some logic with config getters
}
}
You should use annotation
#PostConsturct in a separate method and put the usage of config getters there, like:
#PostConstruct
private void postConstruct() {
// some logic with config getters
}
Is it possible to inject a mock service into a prototype bean using the #Autowired constructor? I realize I could switch to setter injection but I would prefer to use the constructor if possible.
#Component
#Scope(value = "prototype")
public class Prototype {
private DependantService dependantService;
#Autowired
public Prototype(DependantService dependantService) {
this.dependantService = dependantService;
}
}
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
// How can I inject the mock service?
ctx.getBean(Prototype.class);
}
}
Turns out there is an overloaded version of the getBean method that accepts arguments. I would downvote my on question if I could.
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
Prototype p = ctx.getBean(Prototype.class, dependantService);
// Test p
}
}
If you want to speed up your unit tests, [and do true isolated unit testing,] I suggest taking a look at the #InjectMocks mockito annotation. #SpringBootTest fires up the Spring container which is pretty cumbersome.
#Controller
public class MyController {
#Inject
private Logger log;
public methodThatNeedsTesting(){
log.info("hey this was called");
}
}
#TestInstance(Lifecycle.PER_CLASS)
#ExtendWith({ MockitoExtension.class })
class MyControllerTest {
#Mock
private Logger log;
#InjectMocks
private MyController myController;
#Test
void test_methodThatNeedsTesting() throws Exception {
myController.methodThatNeedsTesting();
// myController will not throw an NPE above because the log field has been injected with a mock
}
My goal is to use an in-memory database for these unit tests, and those dependancies are listed as:
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")
So that the repository instance actually interacts with a DB, and I dont just mock return values.
The problem is that when I run my unit test, the repository instance inside the service instance is null.
Why is that? Am I missing some annotation on the unit test class to initialise the repository instance?
This is the console output when running my unit test:
null
java.lang.NullPointerException
at com.my.MyService.findAll(MyService.java:20)
at com.my.MyTest.testMy(MyTest.java:23)
My unit test class:
public class MyTest {
#MockBean
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService();
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
My service class:
#Service
public class MyService {
#Autowired
MyRepository myRepository;
public List<MyEntity> findAll() {
System.out.println(myRepository); // null
return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
}
#Transactional
public MyEntity create(MyEntity myEntity) {
myRepository.save(myEntity);
return myEntity;
}
}
My repository class:
#Repository
public interface MyRepository extends CrudRepository<MyEntity, Long> {
}
My entity class:
#Entity
public class MyEntity {
#Id
#GeneratedValue
public Long id;
}
Why is that? Am I missing some annotation on the unit test class to initialise the repository instance?
Basically yes :)
You need to initialise a Spring Context by Annotating your Testclass with #SpringBootTest
The other Problem you have is that you create your MyService Object manually.
By doing so SpringBoot has no chance to inject any Bean for you. You can fix this by simply injecting your MyService in your Testclass. Your Code should look something like this:
#SpringBootTest
public class MyTest {
#Autowired
private MyService myService;
#Test
void testMy() {
int size = myService.findAll().size();
assertEquals(0, size);
}
}
To use #MockBean annotation, you have to use SpringRunner to run the test. Use #RunWith Annotation on top of your test class and pass SpringRunner.class.
#RunWith(SpringRunner.class)
public class MyTest {
#MockBean
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService();
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
The problem here is your service implementation. Using #Autowired to inject the dependency will work when you run the whole app, but it do not allow you to inject a custom dependency when you'll need it, and a good example of this is testing.
Change your service implementation to:
#Service
public class MyService {
private MyRepository myRepository;
public MyService(MyRepository myRepository){
this.myRepository = myRepository;
}
public List<MyEntity> findAll() {
System.out.println(myRepository); // null
return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
}
#Transactional
public MyEntity create(MyEntity myEntity) {
myRepository.save(myEntity);
return myEntity;
}
}
This constructor will be called by spring. Then change your test to:
public class MyTest {
#Mock
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService(myRepository);
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
Note I have replaced #MockBean to #Mock as the previous annotation is for injecting a mock bean into the spring context, which is not needed if you're doing unit testing. If you want to boot spring context (which I would not recommend you) you need to configure your test class with #SpringBootTest or some of the other available alternatives. That will convert your test into an integration test.
PD: This test will not work if you don't provide a mock to myRepository.findAll(). Mockito default behaviour is to return null, but you're expecting it to return 0, so you'll need to do something like given(myRepository.findAll()).willReturn(0).
I believe you wish to write an integration test. Here you could remove the MockBean annotation and simply autowire your repository. Also, run with The SpringRunner class.
#RunWith(SpringRunner.class)
public class MyTest {
#Autowired
MyRepository myRepository;
#Autowired
MyService myService
#Test
void testMy() {
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
This should work
My team is currently building a SpringBoot API. Below is a snippet of how my team is mocking for our controller tests. This works when we have plain classes as dependencies.
#RunWith(SpringRunner.class)
#SpringBootTest
public class TestController {
private MockMvc mockMvc;
#Mock
private MyService myService;
#Autowired
#InjectMocks
private MyController myController;
#Before
public void setup() {
//Build the controller mock handler
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
We cannot figure out how to mock a #Resource like below for a similar test.
#Resource(name = "domainNameToCode")
private Map<String, Integer> domainCodes;
As the domainCodes is private your controller needs to have a way to set value for it either constructor or a setter.
Setter
#Autowired()
#Qualifier("domainNameToCode")
public void setDomainCodes(Map<String, Integer> domainCodes) {
...
}
Constructor
#Autowired()
#Qualifier("domainNameToCode")
public MyController(Map<String, Integer> domainCodes){
}
In your tests, now you can inject the mocks.
If you don't want to edit your source, then you can use reflection to set a private instance variable on an object
//Using reflection to set a private field on an object
FieldUtils.writeField(controllerInstance, "domainCodes", new HashMap<>()) ;