I have an integration which instantiates a service and that service has an Autowired dependency on a bean I'm trying to mock.
The problem is the service is getting instantiated before the autowired bean is and causing NPE. How can I ensure DependencyINeed is initialized before the MyClass in the example below?
Service
#Service
public class MyClass {
#Autowired private DependencyINeed dependency;
#Autowired
public MyClass(
#Value("${thing1}") int t1,
#Value("${thing2}") String t2) {
}
Method call yielding NPE
public class MyClass {
....
public void randomFunction() {
dependency.methodCall() <-- NPE
}
}
Test
#ContextConfiguration(classes = TestConfiguration.class)
#Import({TestConfiguration.class})
#SpringBootTest(
classes = {TestConfiguration.class, DependencyINeed.class, MyClass.class})
public class MyCoolIntegrationTest {
#Autowired private DependencyINeed dependency;
#Autowired private MyClass client;
Test Configuration
#TestConfiguration
public class MyTestConfiguration {
#MockBean private DependencyINeed dep;
#Bean
public DependencyINeed initDep() {
....
return dep;
}
}
Try to delete yout test config, and make yout Test class look like this:
#ContextConfiguration(classes = TestConfiguration.class)
#Import({TestConfiguration.class})
#SpringBootTest(
classes = {TestConfiguration.class, DependencyINeed.class, MyClass.class})
public class MyCoolIntegrationTest {
#MockBean private DependencyINeed dependency;
#Autowired private MyClass client;
#BeforeEach
public void beforeEach() {
Mockito.when(dependency.SOMEHING()).thenReturn(YOUR STUFF);
}
}
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 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
}
I have a rest controller which I am trying to Unit Test:
It has a few dependencies autowired
#RestController
#RequestMapping("/test")
public class TestController {
private final Dep1 dep1;
private final Dep2 dep2;
private final Dep3 dep3;
public TestController(final Dep1 dep1,
final Dep2 dep2,
final Dep3 dep3) {
this.dep1 = dep1;
this.dep2 = dep2;
this.dep3 = dep3;
}
}
I recently added dep3
#Service
public class Dep3 {
private final IValidator validator;
public Dep3(final IValidator validator) {
this.validator= validator;
}
public void validate(final Request req) {
validator.validate(req);
}
}
Dep3 has its own Autowired Dependency IValidator
Here is my test class:
#WebMvcTest(TestController.class)
public class TestControllerTest {
#MockBean
private Dep1 dep1;
#MockBean
private Dep2 dep2;
#MockBean
private Dep3 dep3;
#Autowired
private MockMvc mockMvc;
#Test
public void someTest() throws Exception {
}
#TestConfiguration
static class InnerConfiguration {
#Bean
IValidator validator() {
return new SomeValidator();
}
}
}
For my test, I need the code to actually run the dep3.validate(..) with the implementation SomeValidator(). I am unsure how to achieve this. Perhaps I am missing an annotation?
Don't mock Dep3. Instead configure Dep3 bean in #TestConfiguration:
#TestConfiguration
static class InnerConfiguration {
#Bean
Dep3 dep3() {
return new Dep3(validator());
}
#Bean
IValidator validator() {
return new SomeValidator();
}
}
Spring will inject fully configured Dep3 into TestController
You have to configure your Mock correctly.
Something like this:
Mockito.when(dep3.validate(Mockito.any(Request.class))).thenReturn(new SomeValidator());
You can do this in your test method or in the setup method before the actual call.
my application normally works fine, but when I run tests, or build application by maven, application is shutting down due tests with errors java.lang.NullPointerException. I debugged it and find out my that my beans in service layer are not Autowired and they are null.
Here is my class with tests:
public class CompanyServiceSimpleTest {
private CompanyService companyService;
#Before
public void setUp() {
companyService = new CompanyServiceImpl();
}
// Here is sample test
#Test
public void testNumberOfCompanies() {
Assert.assertEquals(2, companyService.findAll().size());
}
}
companyService is initialized, but beans in it not. Here is CompanyServiceImpl:
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyRepository companyRepository; // is null
#Autowired
private NotificationService notificationService; // is null
#Override
public List<CompanyDto> findAll() {
List<CompanyEntity> entities = companyRepository.find(0, Integer.MAX_VALUE);
return entities.stream().map(Translations.COMPANY_DOMAIN_TO_DTO).collect(Collectors.toList());
}
// ... some other functions
}
So when is called companyRepository.find() applications crashes. Here is repository class:
#Repository
#Profile("inMemory")
public class CompanyInMemoryRepository implements CompanyRepository {
private final List<CompanyEntity> DATA = new ArrayList<>();
private AtomicLong idGenerator = new AtomicLong(3);
#Override
public List<CompanyEntity> find(int offset, int limit) {
return DATA.subList(offset, Math.min(offset+limit, DATA.size()));
}
// ... some others functions
}
I have set up profile for that service but I had that VM options in Idea:
-Dspring.profiles.active=develpment,inMemory
So it should works.
To make autowiring work it has to be a Spring integration test. You have to anotate your test class with:
#RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration(classes = {MyApplicationConfig.class})
If it is a Spring Boot app e.g.:
#RunWith(SpringJUnit4ClassRunner.class) and #SpringBootTest(classes = {MyApp.class, MyApplicationConfig.class}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
More on this topic: http://www.baeldung.com/integration-testing-in-spring and http://www.baeldung.com/spring-boot-testing
You are not configuring Spring in your TestClasses, so it's impossible to inject anything...
Try configurin your class with
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "path to your config xml" })
A little example:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:config/applicationContext.xml"})
public class MyTest {
#Autowired
private MyClass myInjectedClass;
#Test
public void someTest() {
assertNotNull(myInjectedClass);
}
}
I am trying to write a test for this Java SpringBoot's class:
https://github.com/callistaenterprise/blog-microservices/blob/master/microservices/composite/product-composite-service/src/main/java/se/callista/microservices/composite/product/service/ProductCompositeIntegration.java
Specifically, I am trying to "mock" this method call:
URI uri = util.getServiceUrl("product");
I figured out I should "mock" the ServiceUtils object in order to do this. I tried this using the #Mock and #InjectMocks annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
public class ProductCompositeIntegrationTest {
#InjectMocks
#Autowired
private ProductCompositeIntegration productIntegration;
#Autowired
private RestTemplate restTemplate;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void myTest() {
Mockito.when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
ResponseEntity<Iterable<Product>> products = productIntegration.getAllProducts();
}
}
But this way it still calls the original ServiceUtils object, and not the "mocked" one. Also tried without the #Autowired annotation at the ProductCompositeIntegration, but this results in a NullPointerException.
What am I doing wrong?
My main class looks like this:
#SpringBootApplication
#EnableCircuitBreaker
#EnableDiscoveryClient
public class ProductCompositeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductCompositeServiceApplication.class, args);
}
}
The ServiceUtils object that I am trying to mock is specified in a class, annotated with Spring's #Component annotation to inject it into the other classes using #Autowired.
After a lot of trial and error I managed to solve this problem.
I dropped the
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
annotations aboved the test class.
I marked the class that I was testing with #InjectMocks and the dependencies with #Mock:
public class ProductCompositeIntegrationTest {
#InjectMocks
private ProductCompositeIntegration productIntegration;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
private RestTemplate restTemplate = new RestTemplate();
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
productIntegration.setRestTemplate(restTemplate);
}
#Test
public void someTests() {
when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
//Test code...
}
}
I'm not sure if this is the best approach ("the Spring way"), but this worked for me.
This article made it all clear to me: http://rdafbn.blogspot.be/2014/01/testing-spring-components-with-mockito.html
You have to write a FactoryBean like
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
#Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
#Override
public Class<?> getObjectType() {
return classToBeMocked;
}
#Override
public boolean isSingleton() {
return true;
}
}
In your test-context.xml you have to add the following lines.
<bean id="serviceUtilMock" class="MockitoFactoryBean">
<constructor-arg value="your.package.ServiceUtil" />
</bean>
If you don't use XML configuration, then you have to add the equivalent to above in your Java configuration.