NullPointerException when testing Singleton in Java EE - java

I want to test the getCitation() method using jUnit:
#Singleton
public class QuotesLoaderBean {
Properties quotes;
Properties names;
#PostConstruct
public void init() {
InputStream quotesInput = this.getClass().getClassLoader().getResourceAsStream("quotes.properties");
InputStream namesInput = this.getClass().getClassLoader().getResourceAsStream("names.properties");
quotes = new Properties();
names = new Properties();
try {
quotes.load(quotesInput);
names.load(namesInput);
} catch (IOException ex) {
Logger.getLogger(QuotesLoaderBean.class.getName()).log(Level.SEVERE, null, ex);
}
}
public Citation createCitation(String quote) {
Citation citation = new Citation();
citation.setQuote(quote);
citation.setWho(getName());
return citation;
}
public Citation getCitation() {
Citation citation = new Citation();
citation.setQuote(getQuote());
citation.setWho(getName());
return citation;
}
In the Test File I want to Inject the Singleton and use it in the test method. But then I get the NullPointerException:
public class QuoteServiceTest {
#Inject
QuotesLoaderBean quotesLoaderBean;
public QuoteServiceTest() {
}
#BeforeClass
public static void setUpClass() {
}
#AfterClass
public static void tearDownClass() {
}
#Before
public void setUp() {
}
#After
public void tearDown() {
}
#Test
public void whenGetQuote_thenQuoteShouldBeReturned() {
quotesLoaderBean.getQuote();
}
}
The test method is not finished, nut I just want to show the exception that occurs when I call a method from the Singleton. In another service class i can easily inject the class and call the methods.

Injection is handled by a DI-enabled container in execution time.
When you deploy your entire application, a container is set and injection works fine.
When executing unit tests, none of the services are launched, and any #Inject will end up with the variable set to null, because no container will be launched either.
So, in order to test your code, you may want to build the service inside setUp method:
public class QuotesServiceTest {
QuotesLoaderBean quotesLoaderBean;
// ...
#Before
public void setUp() {
quotesLoaderBean = new QuotesLoaderBean();
// call init method after construction
quotesLoaderBean.init();
}
// ...
}

Related

PowerMockito null pointer when trying to use ApplicationContext

I have a class name ServiceLocator
public class ServiceLocator implements ApplicationContextAware {
private transient ApplicationContext _applicationContext;
private static ServiceLocator _instance = new ServiceLocator();
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
_instance._applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return _instance._applicationContext;
}
public static Object findService(String serviceName) {
return _instance._applicationContext.getBean(serviceName);
}
}
I am trying to use that class to find Service into Approver class methods
public class ApproverService extends AbstractDataService implements IApproverService {
public void updateCompletedInboxStatus(String status) {
IInboxService inboxService = (IInboxService)ServiceLocator.findService("inboxService");
InboxItem inboxItem = inboxService.getInboxItem("test");
inboxItem.setWorkItemStatus(status);
inboxService.saveInboxItem(inboxItem);
}
}
With that code i am trying to write Junit with PowerMockRunner
#RunWith(PowerMockRunner.class)
#PrepareForTest({ApproverService.class})
public class ApproverServiceTest {
#InjectMocks
ApproverService approverService;
#Mock
IInboxService inboxService;
#Mock
ServiceLocator serviceLocator;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void updateCompletedInboxStatus() {
RequestAccessHeader reqHdr = new RequestAccessHeader();
reqHdr.setRequestStatus(AccessConstants.REQ_STATUS_HOLD_INT);
String status = "test";
PowerMockito.mockStatic(ServiceLocator.class);
when(serviceLocator.findService("inboxService")).thenReturn(inboxService);
approverService.updateCompletedInboxStatus(status);
}
}
But I am getting null pointer
java.lang.NullPointerException
at com.alnt.fabric.common.ServiceLocator.findService(ServiceLocator.java:25)
at com.alnt.access.approver.service.ApproverServiceTest.updateCompletedInboxStatus(ApproverServiceTest.java:80)
Please help me to find the solution for that issue.
The static method is obviously not mocked.
The problem is most probably because you haven't add the to-be-mocked class in #PrepareForTest
Change it to #PrepareForTest({ApproverService.class, ServiceLocator.class})
Off-topics:
Although it compiles, calling static method by instance reference is not a good practice. Therefore the line should be when(ServiceLocator.findService(...)).thenReturn(inboxService).
Another problem is, you tried to use Singleton pattern but in wrong way. A singleton is suppose to return you an instance so the caller can call its instance method. Your findService is preferably an instance method and to be called as ServiceLocator.getInstance().findService(...). To further improve, unless you really need it to be a singleton, you should make it a normal object instance and inject to objects that need it (given you are already using Spring, I see no reason making a Singleton)
The setup for the static method is not mocked correctly
#RunWith(PowerMockRunner.class)
#PrepareForTest({ServiceLocator.class}) //Prepare static class for mock
public class ApproverServiceTest {
#Mock
IInboxService inboxService;
#Mock
InboxItem item;
#InjectMocks
ApproverService approverService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void updateCompletedInboxStatus() {
//Arrange
String status = "test";
PowerMockito.mockStatic(ServiceLocator.class);
when(ServiceLocator.findService("inboxService")) //<-- NOTE static call
.thenReturn(inboxService);
when(inboxService.getInboxItem("test")).thenReturn(item);
//Act
approverService.updateCompletedInboxStatus(status);
//...
}
}
Reference Mocking Static Method
The subject under test should actually be refactored to avoid the service locator anit-pattern / code smell and should follow explicit dependency principle via constructor injection.
public class ApproverService extends AbstractDataService implements IApproverService {
private IInboxService inboxService;
#Autowired
public ApproverService(IInboxService inboxService){
this.inboxService = inboxService;
}
public void updateCompletedInboxStatus(String status) {
InboxItem inboxItem = inboxService.getInboxItem("test");
inboxItem.setWorkItemStatus(status);
inboxService.saveInboxItem(inboxItem);
}
}
That way the subject class is genuine about what it needs to perform its function correctly,
And the test can then be refactored accordingly
#RunWith(PowerMockRunner.class)
public class ApproverServiceTest {
#Mock
IInboxService inboxService;
#Mock
InboxItem item;
#InjectMocks
ApproverService approverService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void updateCompletedInboxStatus() {
//Arrange
String status = "test";
when(inboxService.getInboxItem("test")).thenReturn(item);
//Act
approverService.updateCompletedInboxStatus(status);
//...
}
}

Spring bean scope for "one object per test method"

I have a test utility for with I need to have a fresh instance per test method (to prevent that state leaks between tests). So far, I was using the scope "prototype", but now I want to be able to wire the utility into another test utility, and the wired instances shall be the same per test.
This appears to be a standard problem, so I was wondering if there is a "test method" scope or something similar?
This is the structure of the test class and test utilities:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTest {
#Autowired
private TestDriver driver;
#Autowired
private TestStateProvider state;
// ... state
// ... methods
}
#Component
#Scope("prototype") // not right because MyTest and TestStateProvider get separate instances
public class TestDriver {
// ...
}
#Component
public class TestStateProvider {
#Autowired
private TestDriver driver;
// ...
}
I'm aware that I could use #Scope("singleton") and #DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) but this refreshes more than I need – a new TestDriver instance for each test would be enough. Also, this approach is error-prone because all tests using the TestDriver would need to know that they also need the #DirtiesContext annotation. So I'm looking for a better solution.
It is actually pretty easy to implement a testMethod scope:
public class TestMethodScope implements Scope {
public static final String NAME = "testMethod";
private Map<String, Object> scopedObjects = new HashMap<>();
private Map<String, Runnable> destructionCallbacks = new HashMap<>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
destructionCallbacks.put(name, callback);
}
#Override
public Object remove(String name) {
throw new UnsupportedOperationException();
}
#Override
public String getConversationId() {
return null;
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
public static class TestExecutionListener implements org.springframework.test.context.TestExecutionListener {
#Override
public void afterTestMethod(TestContext testContext) throws Exception {
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) testContext
.getApplicationContext();
TestMethodScope scope = (TestMethodScope) applicationContext.getBeanFactory().getRegisteredScope(NAME);
scope.destructionCallbacks.values().forEach(callback -> callback.run());
scope.destructionCallbacks.clear();
scope.scopedObjects.clear();
}
}
#Component
public static class ScopeRegistration implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
factory.registerScope(NAME, new TestMethodScope());
}
}
}
Just register the test execution listener, and there will be one instance per test of all #Scope("testMethod") annotated types:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestExecutionListeners(listeners = TestMethodScope.TestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
public class MyTest {
#Autowired
// ... types annotated with #Scope("testMethod")
}
I ran into the same problem some time ago and came to this solution:
Use Mocks
I wrote some methods to create specific mockito settings to add behavior to each mock.
So create a TestConfiguration class with following methods and bean definition.
private MockSettings createResetAfterMockSettings() {
return MockReset.withSettings(MockReset.AFTER);
}
private <T> T mockClass(Class<T> classToMock) {
return mock(classToMock, createResetAfterMockSettings());
}
and your bean definition will look like:
#Bean
public TestDriver testDriver() {
return mockClass(TestDriver .class);
}
MockReset.AFTER is used to reset the mock after the test method is run.
And finally add a TestExecutionListeners to your Test class:
#TestExecutionListeners({ResetMocksTestExecutionListener.class})

Stop Spring Boot Test from hitting #PostContruct of SpringBootApplication class

I have a SpringBootApplication class that has a #PostConstruct method like that (it initializes the type of database connection):
#SpringBootApplication
public class SpringBootApp extends WebMvcConfigurerAdapter {
public static boolean workOffline = false;
private boolean setupSchema = false;
private IGraphService graphService;
private DbC conf;
#Autowired
public SpringBootApp(IGraphService graphService, DbC conf)
{
this.graphService = graphService;
this.conf = conf;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootApp.class, args);
}
#PostConstruct
public void initializeDB() {
if (workOffline) {
conf.setupOfflineEnvironment();
return;
}
else {
conf.setupProdEnvironment();
}
if (setupSchema) {
graphService.setupTestUsers();
}
}
}
I am also using Spring Boot Tests that extend this base class:
#RunWith(SpringRunner.class)
#Ignore
#SpringBootTest
public class BaseTest {
#Before
public void beforeTest() {
if (SpringBootApp.workOffline) {
conf.setupOfflineEnvironment();
} else {
conf.setupTestEnvironment();
}
graphService.setupTestUsers();}
#After
public void afterTest() {
graphService.deleteAllData();
}
}
My tests are under tests/ while my source code is under src/
Unfortunately there are cases that beforeTest() will execute before #PostConstuct and there are cases that it will execute after.. Is there a way to make my tests run with #SprinbBootTest without entering/constructiong the SpringBootApp class at all?
Thanks!
As requested, here's an attempt using spring properties (without an IDE handy, so please forgive any mistakes)
You can set a property for your test using SpringBootTest#properties
#RunWith(SpringRunner.class)
#Ignore
#SpringBootTest(properties="springBootApp.workOffline=true")
public class BaseTest {
#Before
public void beforeTest() { /* setup */ }
#After
public void afterTest() { /* teardown */ }
}
Now that we know that we can set the spring property in the test, we can set up the application to use the property:
#SpringBootApplication
public class SpringBootApp extends WebMvcConfigurerAdapter {
#Value("${springBootApp.workOffline:false}")
private boolean workOffline = false;
#Value("${springBootApp.setupSchema:false}")
private boolean setupSchema = false;
#PostConstruct
public void initializeDB() {
if (workOffline) {
// ...
} else {
// ...
}
if (setupSchema) {
// ...
}
}
}
As I've written this answer I've noticed a couple of things:
If you're only setting workOffline so you can run tests, then you probably just want to move the database setup actions out of the application and into the BaseTest class instead.
Same goes for setupSchema
If you are executing sql for your tests, don't forget about #Sql and #SqlGroup: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#__sql
Anyway, good luck!

Spring JavaConfig: Retrieving beans by calling Configuration class/bean "directly"

I am wondering if this will get me into trouble or if it is a bad idea:
#Configuration
public class MyConfig {
#Bean
public SomeBean1 someBean1() {
return ...
}
#Bean
public SomeBean2 someBean2() {
return ...
}
}
public class Main {
public static void main(String[] args) throws Throwable {
ApplicationContext ctx = new AnnotationConfigApplicationContext(HubBrokerConfig.class);
MyConfig conf = ctx.getBean(MyConfig.class);
conf.someBean1().doSomething();
conf.someBean2().doSomething();
}
}
You may wonder why I would do as above and not:
public class Main {
public static void main(String[] args) throws Throwable {
ApplicationContext ctx = new AnnotationConfigApplicationContext(HubBrokerConfig.class);
ctx.getBean(SomeBean1.class)).doSomething();
ctx.getBean(SomeBean2.class)).doSomething();
}
}
I do not like the second method as much, as it does not catch as many errors at compile-time. For example if I do ctx.getBean(SomeNonBean.class) I would not get compile-time errors. Also if someBean1() were private, the compiler would catch the error.
The preferred method would be to have
#Autowired
private SomeBean1 somebean1;
#Autowired
private SomeBean2 somebean2;
This is even cleaner, makes testing simpler, and avoids issues such as unnecessarily instantiating more copies than necessary.

Spring Transactional Parameterized Test and Autowiring

Is there a way to get a class that extends AbstractTransactionalJUnit4SpringContexts to play nicely with JUnit's own #RunWith(Parameterized.class), so that fields marked as Autowired get wired in properly?
#RunWith(Parameterized.class)
public class Foo extends AbstractTransactionalJUnit4SpringContextTests {
#Autowired private Bar bar
#Parameters public static Collection<Object[]> data() {
// return parameters, following pattern in
// http://junit.org/apidocs/org/junit/runners/Parameterized.html
}
#Test public void someTest(){
bar.baz() //NullPointerException
}
}
See http://jira.springframework.org/browse/SPR-5292
There is a solution.
You can use a TestContextManager from Spring. In this example, I'm using Theories instead of Parameterized.
#RunWith(Theories.class)
#ContextConfiguration(locations = "classpath:/spring-context.xml")
public class SeleniumCase {
#DataPoints
public static WebDriver[] drivers() {
return new WebDriver[] { firefoxDriver, internetExplorerDriver };
}
private TestContextManager testContextManager;
#Autowired
SomethingDao dao;
private static FirefoxDriver firefoxDriver = new FirefoxDriver();
private static InternetExplorerDriver internetExplorerDriver = new InternetExplorerDriver();
#AfterClass
public static void tearDown() {
firefoxDriver.close();
internetExplorerDriver.close();
}
#Before
public void setUpStringContext() throws Exception {
testContextManager = new TestContextManager(getClass());
testContextManager.prepareTestInstance(this);
}
#Theory
public void testWork(WebDriver driver) {
assertNotNull(driver);
assertNotNull(dao);
}
}
I found this solution here : How to do Parameterized/Theories tests with Spring
You can use SpringClassRule and SpringMethodRule for this purpose
#RunWith(Parameterized.class)
#ContextConfiguration(...)
public class FooTest {
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Autowired
private Bar bar
#Parameters
public static Collection<Object[]> data() {
// return parameters, following pattern in
// http://junit.org/apidocs/org/junit/runners/Parameterized.html
}
#Test
public void someTest() {
bar.baz() //NullPointerException
}
}
No, you can't. The superclass has:
#RunWith(SpringJUnit4ClassRunner.class)
which assures that the tests are run within spring context. If you replace it, you are losing this.
What comes to my mind as an alternative is to extend SpringJunit4ClassRunner, provide your custom functionality there and use it with #RunWith(..). Thus you will have the spring context + your additional functionality. It will call super.createTest(..) and then perform additional stuff on the test.
I've had to handle the transactions programmatically (see http://www.javathinking.com/2011/09/junit-parameterized-test-with-spring-autowiring-and-transactions/):
#RunWith(Parameterized.class)
#ContextConfiguration(locations = "classpath*:/testContext.xml")
public class MyTest {
#Autowired
PlatformTransactionManager transactionManager;
private TestContextManager testContextManager;
public MyTest (... parameters for test) {
// store parameters in instance variables
}
#Before
public void setUpSpringContext() throws Exception {
testContextManager = new TestContextManager(getClass());
testContextManager.prepareTestInstance(this);
}
#Parameterized.Parameters
public static Collection<Object[]> generateData() throws Exception {
ArrayList list = new ArrayList();
// add data for each test here
return list;
}
#Test
public void validDataShouldLoadFully() throws Exception {
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
status.setRollbackOnly();
try {
... do cool stuff here
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
});
}
Here's how I made it work in Spring Boot 1.5.7:
Add the #RunWith(Parameterized.class) annotation to your class
Inject your dependency as class field with:
#Autowired
private Bar bar;
Add your parameter/s as class fields:
private final int qux;
private final Boolean corge;
private final String grault;
Add a constructor to initialize the parameter/s as follows:
public Foo(int qux, Boolean corge, String grault) throws Exception {
this.qux = qux;
this.corge = corge;
this.grault = grault;
new TestContextManager(getClass()).prepareTestInstance(this);
}
Add a static method data that returns a Collection containing the values of your parameters at each iteration, respecting the order by which they are passed to the constructor:
#Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{ 1, Boolean.FALSE, "Hello" },
{ 2, Boolean.TRUE, null },
{ 3, null, "world" }
});
}
Write your test using the class fields declared above like follows:
#Test public void someTest(){
// Some arrangements
// Some actions
assertThat(myTestedIntValue, is(equalTo(qux));
assertThat(myTestedBooleanValue, is(equalTo(corge));
assertThat(myTestedStringValue, is(equalTo(grault));
}
Inspired by Simon's solution, you can use TestContextManager also with Parameterized runner:
#RunWith(Parameterized.class)
#ContextConfiguration(locations = "classpath:/spring-context.xml")
public class MyTestClass {
#Parameters public static Collection data() {
// return parameters, following pattern in
// http://junit.org/apidocs/org/junit/runners/Parameterized.html
}
#Before
public void setUp() throws Exception {
new TestContextManager(getClass()).prepareTestInstance(this);
}
}
Here is full example
I am not sure about handling #Transactional in this case.

Categories

Resources