SpringTest service class with parameterised constructor using MockitoServer - spring-boot

Background
I have a Test class having some unit tests that started failing after I made some changes in #Service class
#Service
public class WeatherService {
Environment Env
// I added this new autowired field as I created a new component WeatherUtil
#Autowired
WeatherUtil weatherUtil;
public WeatherService(Environment env) {
this.env = env;
}
}
Note that there's no default constructor
#Component
public class WeatherUtil {
// reads api properties from application.yml
#Autowired
private ApiProperties apiProperties;
#PostConstruct
private prepareMapOfProps(){
Map<String, Object> mapOfLatLong = apiProperties.getLatLong();
doSomething(mapOfLatLong);
}
private doSomething(Map<String, Object> mapOfLatLong){...}
}
The testClass definition is as below.
#Tag("unit")
#ExtendWith(MockitoExtension.class)
class TestWeatherApiService {
Environment env;
private static MockWebServer mockServer;
WeatherService weatherService;
#BeforeAll
public static void generalSetup() throws IOException {
mockServer = new MockWebServer();
mockServer.start();
}
#AfterAll
public static void generalTeardown() throws IOException {
mockServer.shutdown();
}
#BeforeEach
public void setup() {
when(env.getProperty("api.disable_ssl_certificate_validation", Boolean.class, false)).thenReturn(true);
weatherService = new WeatherService(env);
}
#Test
public void test1(){
// this test was working fine
mockServer.enqueue(new MockResponse().setBody("[]").addHeader("Content-Type", "application/json"));
// lat,ln set to someValue
weatherService.getWeather(lat, ln);
}
}
I have no clue what's going on as the application is running fine but the test has broken and not sure how to recover as the stacktrace shows Exception in other package.
Any idea on what minimal change can fix this?

Related

Unable to mock a method with Mockito and Spring

I have a class JobDelegate for which i am writing unit tests using mockito. I am unable to mock the HTTPOperations class. I have tried using setter injection from test class as well. But it does not work. Below the latest revision of the code. I tried using Power mock. but none of them was helpful. I am unable to predict which is going wrong.
Unit Test code
#ContextConfiguration(locations= "file:src/main/webapp/WEB-INF/spring-
context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
//#RunWith(PowerMockRunner.class)
/#PowerMockIgnore({ "javax.xml.*", "org.xml.*", "org.w3c.*" })
//#PrepareForTest({ HTTPOperations.class})
public class JobSubmissionDelegateTest{
private static Logger LOGGER = null;
private JobDelegate jobDelegate ;
private JobManager jobImpl;
#InjectMocks
private HTTPOperations operations;
//#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Before
public void setupTests() {
jobDelegate = new JobDelegate();
jobManager = new DBJobManagerImpl();
operations = new HTTPOperations();
jobManager.setHttpOperations(operations);
jobSubmissionDelegate.setJobImpl(jobManager);
//HTTPOperations httpOperationsSpy =spy(HTTPOperations.class);
//doReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}").when(myClassSpy).method1();
MockitoAnnotations.initMocks(this);
}
#Test
public void testExecuteJob() throws IOException {
// PowerMockito.mockStatic(HTTPOperations.class);
Mockito.when(operations.submitHttpPostRequest(any(), anyString())).thenReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}");
//System.out.println("==>"+operations.submitHttpPostRequest(null, ""));
...........
int runID = jobDelegate.executeJob(jobDetails);
System.out.println("Run ID here " + runID);
}
}
public class JobDelegate {
// This is an interface.. and the implementation is passed from spring-
context.xml
#Autowired
private JobManager jobImpl;
public int executeJob(JobDTO jobDto) {
............
return jobImpl.runBatchJob(jobDto);
}
}
public class DBJobManagerImpl implements JobManager{
#Autowired
private URIUtils uriUtils;
#Autowired
private HTTPOperations httpOperations;
#Override
public int runBatchJob(JobDTO jobDto) throws Exception {
UriComponentsBuilder uri = uriUtils.createURI(ConfigUtil.getUrI());
String response = httpOperations.submitHttpPostRequest(uri, runSubmitJson);
System.out.println("Response ==> " +response);
.................
}
}
I was able to resolve the issue using PowerMock.
Below the code
#RunWith(PowerMockRunner.class)
#ContextConfiguration(locations= "file:src/main/webapp/WEB-
INF/Enrichment_Context.xml")
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PowerMockIgnore({ "javax.xml.*", "org.xml.*", "org.w3c.*",
"javax.management.*" })
#PrepareForTest({ HTTPOperations.class})
public class JobDelegateTest {
#Autowired
private JobDelegate jobSubmissionDelegate;
#Test
public void testExecuteJob() throws IOException {
PowerMockito.mockStatic(HTTPOperations.class);
PowerMockito.when(HTTPOperations.submitHttpPostRequest(Mockito.any(),
Mockito.anyString())).thenReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}");
...................
int runID = jobSubmissionDelegate.executeJobSubmission(jobDetails);
}
}

Spring injection: #MockBean #Repository is not injected

I'm trying to #MockBean a #Repository annotated class:
#Repository
public interface ApplicationDao extends MongoRepository<Application, String> {}
I'm injecting it into a #Service annotated class:
#Service
public class AuthorizationService {
private ApplicationDao appsDao;
private List<Application> allowedApplications;
#Autowired
public AuthorizationService(ApplicationDao appsDao) {
this.appsDao = appsDao; //<<MOCKED INJECTED BEAN>>
this.fillApplications();
}
private void fillApplications() {
this.appsDao.findAll() //<<MOCKED method>>
.forEach(entry -> {
this.allowedApplications.put(entry.getName(), entry);
});
}
public bool isAuthorized(Application application) {
return this.allowedApplications
.stream()
.anyMatch(app -> app.getId().equals(application.getId()));
}
}
My test mocking configuration looks like:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class GroupReferencesTest {
private #Autowired AuthorizationService;
private #MockBean ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app)); //<<FAILS>>
}
}
Nevertheless, my mocked object is not injected. I mean, when my AuthorizationService calls its injected ApplicationDao is returns an empty list instead of my mocked list.
I've tried to use #MockBean(name="applicationDao") as well. The behavior is the same.
I've also tried to configure my mocked bean using this code:
#TestConfiguration
public class RestTemplateTestConfiguration {
#Bean("applicationDao")
#Primary
public static ApplicationDao mockApplicationDao() {
ApplicationDao mock = Mockito.mock(ApplicationDao.class);
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(mock.findAll())
.thenReturn(allowedApplications);
return mock;
}
}
However, it doesn't works right.
Application class is:
public class Application {
private String id;
//setters & getters
}
Any ideas?
First things first - the type of test. Answer: Unit test.
You are starting Spring context that manages a lifecycle of AuthorizationService and then you are trying to inject mock. What really happens is that Spring IoC container is injecting a real ApplicationDao (the one managed by Spring IoC container) into the AuthorizationService.
Solution:
Manage lifecyle of AuthorizationService by your test runner (like MockitoJUnitRunner and inject ApplicationDao mock into it):
#RunWith(MockitoJUnitRunner.class)
public class GroupReferencesTest {
private #InjectMocks AuthorizationService authorizationService;
private #Mock ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app));
}
}
Working example
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {AuthorizationService.class})
public class GroupReferencesTest {
#Autowired
private AuthorizationService;
#MockBean
private ApplicationDao applicationDao;
#Test
public void test() {
//given
Mockito.when(applicationDao.findAll()).thenReturn(emptyList());
//when & then
assertTrue(authorizationService.isAuthorized(app));
}
}

Field created in spring component in not initialized with new keyword

I have spring component class annotated with #Component and in it I have field ConcurrentHashMap map, which is init in constructor of component and used in spring stream listener:
#Component
public class FooService {
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
fooMap = new ConcurrentHashMap<>();
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body);
}
}
Listener handle messages sent by rest controller. Can you tell me why I always got there fooMap.put(...) NullPointerException because fooMap is null and not initialzied.
EDIT:
After #OlegZhurakousky answer I find out problem is with async method. When I add #Async on some method and add #EnableAsync I can't anymore use private modificator for my #StreamListener method. Do you have idea why and how to fix it?
https://github.com/schwantner92/spring-cloud-stream-issue
Thanks.
Could you try using #PostConstruct instead of constructor?
#PostConstruct
public void init(){
this.fooMap = new ConcurrentHashMap<>();
}
#Denis Stephanov
When I say bare minimum, here is what I mean. So try this as a start, you'll see that the map is not null and start evolving your app from there.
#SpringBootApplication
#EnableBinding(Processor.class)
public class DemoApplication {
private final Map<String, String> map;
public static void main(String[] args) {
SpringApplication.run(DemoRabbit174Application.class, args);
}
public DemoApplication() {
this.map = new HashMap<>();
}
#StreamListener(Processor.INPUT)
public void sink(String string) {
System.out.println(string);
}
}
With Spring everything has to be injected.
You need to declare a #Bean for the ConcurrentHashMap, that will be injected in you Component. So create a Configuration class like:
#Configuration
public class FooMapConfiguration {
#Bean("myFooMap")
public ConcurrentHashMap<Long, String> myFooMap() {
return new ConcurrentHashMap<>();
}
}
Then modify your Component:
#Component
public class FooService {
#Autowired
#Qualifier("myFooMap")
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body); // <= No more NPE here
}
}

Autowiring not working in springboot application

I am trying to create a Spring boot application with JFrame. I can see my beans in applicationContext but they are not getting autowired. I am unable to find the reason for this issue. Can someone help me with this?
Here is the code:
JavauiApplication - it is showing both userManager and userNameRepository is beans
#SpringBootApplication
public class JavauiApplication implements CommandLineRunner {
#Autowired
private ApplicationContext appContext;
public static void main(String[] args) {
new SpringApplicationBuilder(JavauiApplication.class).headless(false).run(args);
java.awt.EventQueue.invokeLater(() -> new InputNameForm().setVisible(true));
}
#Override
public void run(String... args) throws Exception {
String[] beans = appContext.getBeanDefinitionNames();
Arrays.sort(beans);
for (String bean : beans) {
System.out.println(bean);
}
}
}
InputNameForm.java -> userManager coming null
#Component
public class InputNameForm extends javax.swing.JFrame {
/**
* Creates new form InputNameForm
*/
public InputNameForm() {
initComponents();
}
#Autowired
UserManager userManager;
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
userManager.setName(firstName.getText(), lastName.getText());
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(InputNameForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new InputNameForm().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField firstName;
private javax.swing.JLabel firstNameLabel;
private javax.swing.JTextField lastName;
private javax.swing.JLabel lastNameLabel;
private javax.swing.JButton submitButton;
// End of variables declaration//GEN-END:variables
}
UserManager.java -> userNameRepository is coming null
#Component
public class UserManager {
#Autowired
UserNameRepository userNameRepository;
public void setName(String firstName, String lastName) {
userNameRepository.save(new UserName(firstName, lastName));
System.out.println(userNameRepository.findAllByFirstName(firstName));
}
}
It's a very common problem and it occurs because newcomers don't understand how the IoC container works.
Firstly, BeanDefinitionReader reads metadata about your beans from XML, Annotations(#Component, #Service etc), JavaConfig or Groovy script.
There are several BeanPostProcessor's which is responsible for reading all of these Spring annotation you're writing(#Autowired etc).
BeanFactory creates all BeanPostProcessor's then it creates all of your beans.
What happen if you create your bean with #Autowired dependencies via new operator? Nothing, because it isn't actually a bean. The object you created isn't related to IoC container. You may have the bean already in your ApplicationContext if you marked it with #Component(for example) but the object which was created via new operator wont be processed by Spring(annotations won't work).
Hope this helps.
PS: The lifecycle is simplified.
I had the same problem few days ago. What I undertood was that GUI builders like the one that comes with netbeans will automatically create components using new keyword. This means that those components won't be manage by spring. The code usually loks like this:
private void initComponents() {
jPanel1 = new javax.swing.JPanel(); //This component will not be managed by spring.
//...
}
You could use the following class provided here, to make it work.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
// Autowires the specified object in the spring context
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
The top level SwingApp class:
#SpringBootApplication
public class SwingApp implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(SwingApp.class)
.headless(false).bannerMode(Banner.Mode.OFF).run(args);
}
#Override
public void run(String... args) throws Exception {
SwingUtilities.invokeLater(() -> {
MainFrame frame = new MainFrame();
frame.setVisible(true);
});
}
}
The MainFrame class:
public class MainFrame extends javax.swing.JFrame {
public MainFrame() {
initComponents();
}
private void initComponents() {
//Gui Builder generated code. Bean not managed by spring.
//Thus, autowired inside CustomPanel won't work if you rely on ComponentScan.
jPanel1 = new CustomJPanel();
//...
}
private CustomJPanel jPanel1;
}
The panel class where you want to autowire things:
//#Component //not needed since it wont work with gui generated code.
public class CustomJPanel extends javax.swing.JPanel{
#Autowired
private SomeRepository someRepository
public CustomJPanel(){
BeanProvider.autowire(this); //use someRepository somewhere after this line.
}
}
I have the same problem in a JavaFx project. Service and Component annotated classes were null in UI controllers even if it was shown in context that it was created. Below code worked for me
#Component
public class FxmlLoaderWithContext {
private final ApplicationContext context;
#Autowired
public FxmlLoaderWithContext(ApplicationContext context) {
this.context = context;
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setControllerFactory(context::getBean); //this row ensure services and components to be autowired
}
}
I think it returns null because you using command new to create object, such as new InputNameForm(). When creating object like that, the object isn't managed by Spring. That's why autowired not working.
The solution is registering your class as a bean.
You can use a class like in here.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
And then, in your class InputNameForm constructor, call this:
class InputNameForm() {
BeanProvider.autowire(this);
...
}
And that's it. Spring will take care the rest.

Spring Boot + DynamoDBTypeConverter dependancy injection

I'm trying to get DI working with a sample DynamoDBTypeConverter I'm playing around with and having no luck at all :( My service is always null and throws an error as a result in my jUnit test.
Here's my converter:
#Component
public class ArmTypeConverter implements DynamoDBTypeConverter<String, Arm> {
#Autowired
private ArmRepository armRepository;
#Override
public String convert(Arm Arm) {
return arm.getId();
}
#Override
public Arm unconvert(String id) {
return armRepository.findOne(id);
}
}
My application main:
#SpringBootApplication
#ComponentScan
#EnableSpringConfigured
#EnableLoadTimeWeaving(aspectjWeaving=EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableAutoConfiguration
public class ArmApplication implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(ArmApplication.class, args);
}
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() throws Throwable {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
}
My service:
#Service
public class ArmServiceImpl implements ArmService {
#Autowired
private ArmRepository armRepository;
#Override
public Arm create(String length, Set<Register> registers) {
Date now = new Date();
Arm arm = new Arm();
arm.setLength("85cm");
return armRepository.save(arm);
}
}
My Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class ArmServiceTests {
#Autowired
private ArmService armService;
#Autowired
private TorsoService torsoService;
private Arm arm;
#Before
public void before() {
arm = armService.create("85cm", null);
torsoService.create("150cm", arm);
}
#After
public void after() {
// do nothing for now...
}
#Test
#WithUserDetails("admin#somewhere.com")
public void getArmTest() {
Arm c = armService.getArm(arm.getId());
assertThat(c).isNotNull();
assertThat(c.getId()).isEqualTo(arm.getId());
}
}
What am I doing wrong?
The issue was that I didn't have load time weaving configured properly

Resources