I'm quite new to SpringBoot. I have created a sample application with a service class.
Below is my SpringBootApplication class
#SpringBootApplication
public class SampleApplication {
#Autowired
static AWSService awsService;
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
awsService.getCertificate(); // Getting an NPE at this point
}
}
The AWSService class
#Service
public class AWSService {
public AWSService() {
}
private final Log log = new Log(getClass().getSimpleName());
public void getCertificate() {
String accessKey="";
String secretKey="";
try {
Scanner awsCredentials = new Scanner(new File(Constants.AWS_CREDENTIALS));
accessKey=awsCredentials.next();
secretKey=awsCredentials.next();
} catch (FileNotFoundException e) {
e.printStackTrace();
log.error(e.getMessage());
}
BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(accessKey,secretKey);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withCredentials(
new AWSStaticCredentialsProvider(basicAWSCredentials)).build();
S3Object s3object = s3Client.getObject(
new GetObjectRequest(Constants.S3_BUCKET_NAME, Constants.S3_KEY_NAME));
String temporaryCertificatePath = storeCertificate(s3object);
Constants.setKeyStoreFile(temporaryCertificatePath);
}
private String storeCertificate(S3Object s3Object) {
try {
File certificate = File.createTempFile("signingKey",".p12");
OutputStream outputStream = new FileOutputStream(certificate);
byte buffer [] = IOUtils.toByteArray(s3Object.getObjectContent());
outputStream.write(buffer);
certificate.deleteOnExit();
return certificate.getCanonicalPath();
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
}
return null;
}
}
Following is the error I'm getting
Exception in thread "main" java.lang.NullPointerException
2017-02-27 13:24:04.023 at in.juspay.SampleApplication.main(SampleApplication.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
INFO at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
798 at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
I have Autowired the service in my application class, yet I'm getting a NullPointerException. If I understand Spring's #Service properly, the #Autowire should take care of the initialization of the object. Why then, am I getting an NPE at that point?
You cannot autowire static fields. Use application context to reach it.
Can you use #Autowired with static fields?
Very basic thing, static fields gets priority even before bean creation.
That's why you can use static keyword to get a access of an instance.
refer this also Getting java.lang.NullPointerException when calling Method.invoke
Related
TL:DR; When running tests with different #ResourceArgs, the configuration of different tests get thrown around and override others, breaking tests meant to run with specific configurations.
So, I have a service that has tests that run in different configuration setups. The main difference at the moment is the service can either manage its own authentication or get it from an external source (Keycloak).
I firstly control this using test profiles, which seem to work fine. Unfortunately, in order to support both cases, the ResourceLifecycleManager I have setup supports setting up a Keycloak instance and returns config values that break the config for self authentication (This is due primarily to the fact that I have not found out how to get the lifecycle manager to determine on its own what profile or config is currently running. If I could do this, I think I would be much better off than using #ResourceArg, so would love to know if I missed something here).
To remedy this shortcoming, I have attempted to use #ResourceArgs to convey to the lifecycle manager when to setup for external auth. However, I have noticed some really odd execution timings and the config that ends up at my test/service isn't what I intend based on the test class's annotations, where it is obvious the lifecycle manager has setup for external auth.
Additionally, it should be noted that I have my tests ordered such that the profiles and configs shouldn't be running out of order; all the tests that don't care are run first, then the 'normal' tests with self auth, then the tests with the external auth profile. I can see this working appropriately when I run in intellij, and the fact I can tell the time is being taken to start up the new service instance between the test profiles.
Looking at the logs when I throw a breakpoint in places, some odd things are obvious:
When breakpoint on an erring test (before the external-configured tests run)
The start() method of my TestResourceLifecycleManager has been called twice
The first run ran with Keycloak starting, would override/break config
though the time I would expect to need to be taken to start up keycloak not happening, a little confused here
The second run is correct, not starting keycloak
The profile config is what is expected, except for what the keycloak setup would override
When breakpoint on an external-configured test (after all self-configured tests run):
The start() method has now been called 4 times; appears that things were started in the same order as before again for the new run of the app
There could be some weirdness in how Intellij/Gradle shows logs, but I am interpreting this as:
Quarkus initting the two instances of LifecycleManager when starting the app for some reason, and one's config overrides the other, causing my woes.
The lifecycle manager is working as expected; it appropriately starts/ doesn't start keycloak when configured either way
At this point I can't tell if I'm doing something wrong, or if there's a bug.
Test class example for self-auth test (same annotations for all tests in this (test) profile):
#Slf4j
#QuarkusTest
#QuarkusTestResource(TestResourceLifecycleManager.class)
#TestHTTPEndpoint(Auth.class)
class AuthTest extends RunningServerTest {
Test class example for external auth test (same annotations for all tests in this (externalAuth) profile):
#Slf4j
#QuarkusTest
#TestProfile(ExternalAuthTestProfile.class)
#QuarkusTestResource(value = TestResourceLifecycleManager.class, initArgs = #ResourceArg(name=TestResourceLifecycleManager.EXTERNAL_AUTH_ARG, value="true"))
#TestHTTPEndpoint(Auth.class)
class AuthExternalTest extends RunningServerTest {
ExternalAuthTestProfile extends this, providing the appropriate profile name:
public class NonDefaultTestProfile implements QuarkusTestProfile {
private final String testProfile;
private final Map<String, String> overrides = new HashMap<>();
protected NonDefaultTestProfile(String testProfile) {
this.testProfile = testProfile;
}
protected NonDefaultTestProfile(String testProfile, Map<String, String> configOverrides) {
this(testProfile);
this.overrides.putAll(configOverrides);
}
#Override
public Map<String, String> getConfigOverrides() {
return new HashMap<>(this.overrides);
}
#Override
public String getConfigProfile() {
return testProfile;
}
#Override
public List<TestResourceEntry> testResources() {
return QuarkusTestProfile.super.testResources();
}
}
Lifecycle manager:
#Slf4j
public class TestResourceLifecycleManager implements QuarkusTestResourceLifecycleManager {
public static final String EXTERNAL_AUTH_ARG = "externalAuth";
private static volatile MongodExecutable MONGO_EXE = null;
private static volatile KeycloakContainer KEYCLOAK_CONTAINER = null;
private boolean externalAuth = false;
public synchronized Map<String, String> startKeycloakTestServer() {
if(!this.externalAuth){
log.info("No need for keycloak.");
return Map.of();
}
if (KEYCLOAK_CONTAINER != null) {
log.info("Keycloak already started.");
} else {
KEYCLOAK_CONTAINER = new KeycloakContainer()
// .withEnv("hello","world")
.withRealmImportFile("keycloak-realm.json");
KEYCLOAK_CONTAINER.start();
log.info(
"Test keycloak started at endpoint: {}\tAdmin creds: {}:{}",
KEYCLOAK_CONTAINER.getAuthServerUrl(),
KEYCLOAK_CONTAINER.getAdminUsername(),
KEYCLOAK_CONTAINER.getAdminPassword()
);
}
String clientId;
String clientSecret;
String publicKey = "";
try (
Keycloak keycloak = KeycloakBuilder.builder()
.serverUrl(KEYCLOAK_CONTAINER.getAuthServerUrl())
.realm("master")
.grantType(OAuth2Constants.PASSWORD)
.clientId("admin-cli")
.username(KEYCLOAK_CONTAINER.getAdminUsername())
.password(KEYCLOAK_CONTAINER.getAdminPassword())
.build();
) {
RealmResource appsRealmResource = keycloak.realms().realm("apps");
ClientRepresentation qmClientResource = appsRealmResource.clients().findByClientId("quartermaster").get(0);
clientSecret = qmClientResource.getSecret();
log.info("Got client id \"{}\" with secret: {}", "quartermaster", clientSecret);
//get private key
for (KeysMetadataRepresentation.KeyMetadataRepresentation curKey : appsRealmResource.keys().getKeyMetadata().getKeys()) {
if (!SIG.equals(curKey.getUse())) {
continue;
}
if (!"RSA".equals(curKey.getType())) {
continue;
}
String publicKeyTemp = curKey.getPublicKey();
if (publicKeyTemp == null || publicKeyTemp.isBlank()) {
continue;
}
publicKey = publicKeyTemp;
log.info("Found a relevant key for public key use: {} / {}", curKey.getKid(), publicKey);
}
}
// write public key
// = new File(TestResourceLifecycleManager.class.getResource("/").toURI().toString() + "/security/testKeycloakPublicKey.pem");
File publicKeyFile;
try {
publicKeyFile = File.createTempFile("oqmTestKeycloakPublicKey",".pem");
// publicKeyFile = new File(TestResourceLifecycleManager.class.getResource("/").toURI().toString().replace("/classes/java/", "/resources/") + "/security/testKeycloakPublicKey.pem");
log.info("path of public key: {}", publicKeyFile);
// if(publicKeyFile.createNewFile()){
// log.info("created new public key file");
//
// } else {
// log.info("Public file already exists");
// }
try (
FileOutputStream os = new FileOutputStream(
publicKeyFile
);
) {
IOUtils.write(publicKey, os, UTF_8);
} catch (IOException e) {
log.error("Failed to write out public key of keycloak: ", e);
throw new IllegalStateException("Failed to write out public key of keycloak.", e);
}
} catch (IOException e) {
log.error("Failed to create public key file: ", e);
throw new IllegalStateException("Failed to create public key file", e);
}
String keycloakUrl = KEYCLOAK_CONTAINER.getAuthServerUrl().replace("/auth", "");
return Map.of(
"test.keycloak.url", keycloakUrl,
"test.keycloak.authUrl", KEYCLOAK_CONTAINER.getAuthServerUrl(),
"test.keycloak.adminName", KEYCLOAK_CONTAINER.getAdminUsername(),
"test.keycloak.adminPass", KEYCLOAK_CONTAINER.getAdminPassword(),
//TODO:: add config for server to talk to
"service.externalAuth.url", keycloakUrl,
"mp.jwt.verify.publickey.location", publicKeyFile.getAbsolutePath()
);
}
public static synchronized void startMongoTestServer() throws IOException {
if (MONGO_EXE != null) {
log.info("Flapdoodle Mongo already started.");
return;
}
Version.Main version = Version.Main.V4_0;
int port = 27018;
log.info("Starting Flapdoodle Test Mongo {} on port {}", version, port);
IMongodConfig config = new MongodConfigBuilder()
.version(version)
.net(new Net(port, Network.localhostIsIPv6()))
.build();
try {
MONGO_EXE = MongodStarter.getDefaultInstance().prepare(config);
MongodProcess process = MONGO_EXE.start();
if (!process.isProcessRunning()) {
throw new IOException();
}
} catch (Throwable e) {
log.error("FAILED to start test mongo server: ", e);
MONGO_EXE = null;
throw e;
}
}
public static synchronized void stopMongoTestServer() {
if (MONGO_EXE == null) {
log.warn("Mongo was not started.");
return;
}
MONGO_EXE.stop();
MONGO_EXE = null;
}
public synchronized static void cleanMongo() throws IOException {
if (MONGO_EXE == null) {
log.warn("Mongo was not started.");
return;
}
log.info("Cleaning Mongo of all entries.");
}
#Override
public void init(Map<String, String> initArgs) {
this.externalAuth = Boolean.parseBoolean(initArgs.getOrDefault(EXTERNAL_AUTH_ARG, Boolean.toString(this.externalAuth)));
}
#Override
public Map<String, String> start() {
log.info("STARTING test lifecycle resources.");
Map<String, String> configOverride = new HashMap<>();
try {
startMongoTestServer();
} catch (IOException e) {
log.error("Unable to start Flapdoodle Mongo server");
}
configOverride.putAll(startKeycloakTestServer());
return configOverride;
}
#Override
public void stop() {
log.info("STOPPING test lifecycle resources.");
stopMongoTestServer();
}
}
The app can be found here: https://github.com/Epic-Breakfast-Productions/OpenQuarterMaster/tree/main/software/open-qm-base-station
The tests are currently failing in the ways I am describing, so feel free to look around.
Note that to run this, you will need to run ./gradlew build publishToMavenLocal in https://github.com/Epic-Breakfast-Productions/OpenQuarterMaster/tree/main/software/libs/open-qm-core to install a dependency locally.
Github issue also tracking this: https://github.com/quarkusio/quarkus/issues/22025
Any use of #QuarkusTestResource() without restrictToAnnotatedClass set to true, means that the QuarkusTestResourceLifecycleManager will be applied to all tests no matter where the annotation is placed.
Hope restrictToAnnotatedClass will solve the problem.
I have a Spring Boot app which uses JMS to connect to a queue and listen for incoming messages. In the app I have an integration test which sends some messages to a queue, then makes sure that the things that are supposed to happen when the listener picks up a new message actually happen.
I have annotated my test class with #DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
to ensure my database is clean after each test. Each test passes when it is run in isolation. However when running them all together after the first test passes successfully the next test fails with the exception below when the code under test attempts to save an entity to the database:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at com.sun.proxy.$Proxy95.handleWorkflowEvent(Unknown Source) ~[na:na]
at com.mottmac.processflow.infra.jms.EventListener.onWorkflowEvent(EventListener.java:51) ~[classes/:na]
at com.mottmac.processflow.infra.jms.EventListener.onMessage(EventListener.java:61) ~[classes/:na]
at org.apache.activemq.ActiveMQMessageConsumer.dispatch(ActiveMQMessageConsumer.java:1401) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.ActiveMQSessionExecutor.dispatch(ActiveMQSessionExecutor.java:131) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.ActiveMQSessionExecutor.iterate(ActiveMQSessionExecutor.java:202) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:133) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.thread.PooledTaskRunner$1.run(PooledTaskRunner.java:48) [activemq-client-5.14.3.jar:5.14.3]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_77]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_77]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_77]
Caused by: java.lang.IllegalStateException: EntityManagerFactory is closed
at org.hibernate.jpa.internal.EntityManagerFactoryImpl.validateNotClosed(EntityManagerFactoryImpl.java:367) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.internal.EntityManagerFactoryImpl.internalCreateEntityManager(EntityManagerFactoryImpl.java:316) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.internal.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:286) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.springframework.orm.jpa.JpaTransactionManager.createEntityManagerForTransaction(JpaTransactionManager.java:449) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:369) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
... 17 common frames omitted
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestGovernance.class })
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
public class ActivitiIntegrationTest
{
private static final String TEST_PROCESS_KEY = "oneTaskProcess";
private static final String FIRST_TASK_KEY = "theTask";
private static final String NEXT_TASK_KEY = "nextTask";
#Autowired
private JmsTemplate jms;
#Autowired
private WorkflowEventRepository eventRepository;
#Autowired
private TaskService taskService;
#Test
public void workFlowEventForRunningTaskMovesItToTheNextStage() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
Task activeTask = getActiveTask();
assertThat(activeTask.getTaskDefinitionKey(), is(FIRST_TASK_KEY));
sendMessageToUpdateExistingTask(activeTask.getProcessInstanceId(), FIRST_TASK_KEY);
Task nextTask = getActiveTask();
assertThat(nextTask.getTaskDefinitionKey(), is(NEXT_TASK_KEY));
}
#Test
public void newWorkflowEventIsSavedToDatabaseAndKicksOffTask() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
assertThat(eventRepository.findAll(), hasSize(1));
}
#Test
public void newWorkflowEventKicksOffTask() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
Task activeTask = getActiveTask();
assertThat(activeTask.getTaskDefinitionKey(), is(FIRST_TASK_KEY));
}
private void sendMessageToUpdateExistingTask(String processId, String event) throws InterruptedException
{
WorkflowEvent message = new WorkflowEvent();
message.setRaisedDt(ZonedDateTime.now());
message.setEvent(event);
// Existing
message.setIdWorkflowInstance(processId);
jms.convertAndSend("workflow", message);
Thread.sleep(5000);
}
private void sendMessageToCreateNewInstanceOfProcess(String event) throws InterruptedException
{
WorkflowEvent message = new WorkflowEvent();
message.setRaisedDt(ZonedDateTime.now());
message.setEvent(event);
jms.convertAndSend("workflow", message);
Thread.sleep(5000);
}
private Task getActiveTask()
{
// For some reason the tasks in the task service are hanging around even
// though the context is being reloaded. This means we have to get the
// ID of the only task in the database (since it has been cleaned
// properly) and use it to look up the task.
WorkflowEvent workflowEvent = eventRepository.findAll().get(0);
Task activeTask = taskService.createTaskQuery().processInstanceId(workflowEvent.getIdWorkflowInstance().toString()).singleResult();
return activeTask;
}
}
The method that throws the exception in the application (repository is just a standard Spring Data CrudRepository):
#Override
#Transactional
public void handleWorkflowEvent(WorkflowEvent event)
{
try
{
logger.info("Handling workflow event[{}]", event);
// Exception is thrown here:
repository.save(event);
logger.info("Saved event to the database [{}]", event);
if(event.getIdWorkflowInstance() == null)
{
String newWorkflow = engine.newWorkflow(event.getEvent(), event.getVariables());
event.setIdWorkflowInstance(newWorkflow);
}
else
{
engine.moveToNextStage(event.getIdWorkflowInstance(), event.getEvent(), event.getVariables());
}
}
catch (Exception e)
{
logger.error("Error while handling workflow event:" , e);
}
}
My test configuration class:
#SpringBootApplication
#EnableJms
#TestConfiguration
public class TestGovernance
{
private static final String WORKFLOW_QUEUE_NAME = "workflow";
#Bean
public ConnectionFactory connectionFactory()
{
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
return connectionFactory;
}
#Bean
public EventListenerJmsConnection connection(ConnectionFactory connectionFactory) throws NamingException, JMSException
{
// Look up ConnectionFactory and Queue
Destination destination = new ActiveMQQueue(WORKFLOW_QUEUE_NAME);
// Create Connection
Connection connection = connectionFactory.createConnection();
Session listenerSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer receiver = listenerSession.createConsumer(destination);
EventListenerJmsConnection eventListenerConfig = new EventListenerJmsConnection(receiver, connection);
return eventListenerConfig;
}
}
The JMS message listener (not sure if that will help):
/**
* Provides an endpoint which will listen for new JMS messages carrying
* {#link WorkflowEvent} objects.
*/
#Service
public class EventListener implements MessageListener
{
Logger logger = LoggerFactory.getLogger(EventListener.class);
private WorkflowEventHandler eventHandler;
private MessageConverter messageConverter;
private EventListenerJmsConnection listenerConnection;
#Autowired
public EventListener(EventListenerJmsConnection listenerConnection, WorkflowEventHandler eventHandler, MessageConverter messageConverter)
{
this.eventHandler = eventHandler;
this.messageConverter = messageConverter;
this.listenerConnection = listenerConnection;
}
#PostConstruct
public void setUpConnection() throws NamingException, JMSException
{
listenerConnection.setMessageListener(this);
listenerConnection.start();
}
private void onWorkflowEvent(WorkflowEvent event)
{
logger.info("Recieved new workflow event [{}]", event);
eventHandler.handleWorkflowEvent(event);
}
#Override
public void onMessage(Message message)
{
try
{
message.acknowledge();
WorkflowEvent fromMessage = (WorkflowEvent) messageConverter.fromMessage(message);
onWorkflowEvent((WorkflowEvent) fromMessage);
}
catch (Exception e)
{
logger.error("Error: ", e);
}
}
}
I've tried adding #Transactional' to the test methods and removing it from the code under test and various combinations with no success. I've also tried adding various test execution listeners and I still can't get it to work. If I remove the#DirtiesContext` then the exception goes away and all the tests run without exception (they do however fail with assertion errors as I would expect).
Any help would be greatly appreciated. My searches so far haven't turned up anything, everything suggests that #DirtiesContext should work.
Using #DirtiesContext for this is a terrible idea (imho) what you should do is make your tests #Transactional. I would also suggest to remove the Thread.sleep and use something like awaitility instead.
In theory when you execute a query all pending changes should be committed so you could use awaitility to check for at most 6 seconds to see if something has been persisted in the database. If that doesn't work you can try adding a flush before the query.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestGovernance.class })
#Transactional
public class ActivitiIntegrationTest {
private static final String TEST_PROCESS_KEY = "oneTaskProcess";
private static final String FIRST_TASK_KEY = "theTask";
private static final String NEXT_TASK_KEY = "nextTask";
#Autowired
private JmsTemplate jms;
#Autowired
private WorkflowEventRepository eventRepository;
#Autowired
private TaskService taskService;
#Autowired
private EntityManager em;
#Test
public void workFlowEventForRunningTaskMovesItToTheNextStage() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
await().atMost(6, SECONDS).until(getActiveTask() != null);
Task activeTask = getActiveTask());
assertThat(activeTask.getTaskDefinitionKey(), is(FIRST_TASK_KEY));
sendMessageToUpdateExistingTask(activeTask.getProcessInstanceId(), FIRST_TASK_KEY);
Task nextTask = getActiveTask();
assertThat(nextTask.getTaskDefinitionKey(), is(NEXT_TASK_KEY));
}
private Task getActiveTask()
{
em.flush(); // simulate a commit
// For some reason the tasks in the task service are hanging around even
// though the context is being reloaded. This means we have to get the
// ID of the only task in the database (since it has been cleaned
// properly) and use it to look up the task.
WorkflowEvent workflowEvent = eventRepository.findAll().get(0);
Task activeTask = taskService.createTaskQuery().processInstanceId(workflowEvent.getIdWorkflowInstance().toString()).singleResult();
return activeTask;
}
}
You might need / want to polish your getActiveTask a little to be able to return null or maybe this change makes it even behave like you expected it to do.
I just did a single method the others you can probably figure out yourself. Your gain with this approach is probably 2 fold, 1 it will not wait for 5 seconds anymore but less and you don't have to reload your whole application between tests. Both of which should make your tests faster.
Hi I am working on a project and using PrintWriter class for opening and writing in the file. But when I am writing the test case for same it gives following error at Line 153
Wanted but not invoked:
mockPrintWriter.println("ID url1
");
-> at x.y.z.verify(ProcessImageDataTest.java:153)
Actually, there were zero interactions with this mock.
Code: (Uses Lombok Library)
ProcessImageData.java
#Setter
#RequiredArgsConstructor
public class ProcessImageData implements T {
private final File newImageDataTextFile;
#Override
public void execute() {
LineIterator inputFileIterator = null;
try {
File filteredImageDataTextFile = new File(filteredImageDataTextFilepath);
PrintWriter writer = new PrintWriter(newImageDataTextFile);
inputFileIterator = FileUtils.lineIterator(filteredImageDataTextFile, StandardCharsets.UTF_8.displayName());
while (inputFileIterator.hasNext()) {
if(someCondition)
**Line51** writer.println(imageDataFileLine);
//FileUtils.writeStringToFile(newImageDataTextFile, imageDataFileLine + NEWLINE, true);
}
}
} catch (Exception e) {
} finally {
LineIterator.closeQuietly(inputFileIterator);
**LINE63** writer.close();
}
}
ProcessImageDataTest.java
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ProcessImageData.class, FileUtils.class, Printwriter.class })
public class ProcessImageDataTest {
private ProcessImageData processImageData;
private static final String FILTERED_IMAGE_DATA_TEXT_FILE_PATH = "filteredFilepath";
private File FILTEREDFILE = new File(FILTERED_PATH);
private static final File IMAGE__FILE = new File("imageFilePath");
private LineIterator lineIterator;
#Mock
private PrintWriter mockPrintWriter;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
processImageData = new ProcessImageData(Palettes_file, FILTERED_PATH, IMAGE_FILE);
PowerMockito.mockStatic(FileUtils.class);
PowerMockito.whenNew(PrintWriter.class).withArguments(IMAGE_FILE).thenReturn(mockPrintWriter);
PowerMockito.when(FileUtils.lineIterator(FILTERED_FILE, StandardCharsets.UTF_8.displayName())).thenReturn(lineIterator);
PowerMockito.when(lineIterator.hasNext()).thenReturn(true, true, false);
}
#Test
public void testTaskWhenIDInDBAndStale() throws IOException {
PowerMockito.when(lineIterator.nextLine()).thenReturn(ID2 + SPACE + URL1, ID1 + SPACE + URL2);
processImageData.execute();
List<String> exepctedFileContentOutput = Arrays.asList(ID2 + SPACE + URL1 + NEWLINE);
verify(exepctedFileContentOutput, 1, 1);
}
#Test
public void testTaskWhenIDNotInDB() throws IOException {
PowerMockito.when(lineIterator.nextLine()).thenReturn(ID2 + SPACE + URL1, ID3 + SPACE + URL2);
processImageData.execute();
List<String> exepctedFileContentOutput = Arrays.asList(ID3 + SPACE + URL2 + NEWLINE);
verify(exepctedFileContentOutput, 1, 1);
}
private void verify(List<String> exepctedFileContentOutput, int fileWriteTimes, int fileReadTimes) throws IOException {
for (String line : exepctedFileContentOutput){
**Line153** Mockito.verify(mockPrintWriter, Mockito.times(fileWriteTimes)).print(line);
}
PowerMockito.verifyStatic(Mockito.times(fileReadTimes));
FileUtils.lineIterator(FILTERED_IMAGE_DATA_TEXT_FILE, StandardCharsets.UTF_8.displayName());
}
}
I am mocking a new operator for PrintWriter also, injecting using beans. What is the mistake I am doing?? I am stuck on it from long time and not getting the error?
Any help is appreciated.
Updated :
I did changes suggested below and updated the code, but now I get the error:
Wanted but not invoked: mockPrintWriter.print("ASIN2 url1 "); ->
at softlines.ctl.ruleExecutor.tasks.ProcessImageDataTest.verify(ProcessImageDataTest.java:153)
However, there were other interactions with this mock: -> at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(ProcessImageData.java:51) ->
at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(ProcessImageData.java:51) ->
at softlines.ctl.ruleExecutor.tasks.ProcessImageData.execute(ProcessImageData.java:58) –
I see 3 issues in your test:
You don't try to mock the correct constructor, indeed in the method execute, you create your PrintWriter with only one argument of type File while you try to mock the constructor with 2 arguments one of type File and the other one of type String.
So the code should rather be:
PowerMockito.whenNew(PrintWriter.class)
.withArguments(IMAGE_FILE)
.thenReturn(mockPrintWriter);
To be able to mock a constructor you need to prepare the class creating the instance which is ProcessImageData in this case, so you need to add ProcessImageData.class in the annotation #PrepareForTest. (I'm not sure ProcessImageDataTest.class is needed there)
The field lineIterator should be annotated with #Mock.
Instead of verifying print with a new line, you should verify directly println without new line it is much less error prone.
I simplified your code to show the idea.
Assuming that ProcessImageData is:
public class ProcessImageData {
private final File newImageDataTextFile;
public ProcessImageData(final File newImageDataTextFile) {
this.newImageDataTextFile = newImageDataTextFile;
}
public void execute() throws Exception{
try (PrintWriter writer = new PrintWriter(newImageDataTextFile)) {
LineIterator inputFileIterator = FileUtils.lineIterator(
newImageDataTextFile, StandardCharsets.UTF_8.displayName()
);
while (inputFileIterator.hasNext()) {
writer.println(inputFileIterator.nextLine());
}
}
}
}
My unit test would then be:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ProcessImageData.class, FileUtils.class})
public class ProcessImageDataTest {
private File file = new File("imageFilePath");
private ProcessImageData processImageData;
#Mock
private PrintWriter mockPrintWriter;
#Mock
private LineIterator lineIterator;
#Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
processImageData = new ProcessImageData(file);
PowerMockito.whenNew(PrintWriter.class)
.withArguments(file)
.thenReturn(mockPrintWriter);
PowerMockito.mockStatic(FileUtils.class);
PowerMockito.when(
FileUtils.lineIterator(file, StandardCharsets.UTF_8.displayName())
).thenReturn(lineIterator);
PowerMockito.when(lineIterator.hasNext()).thenReturn(true, true, false);
}
#Test
public void testExecute() throws Exception {
PowerMockito.when(lineIterator.nextLine()).thenReturn("Foo", "Bar");
processImageData.execute();
Mockito.verify(mockPrintWriter, Mockito.times(1)).println("Foo");
Mockito.verify(mockPrintWriter, Mockito.times(1)).println("Bar");
}
}
For more details please refer to How to mock construction of new objects.
how can I add verification in unit test for writer.close?
One way could be to simply check that close() at be called once by adding the next line to your unit test:
Mockito.verify(mockPrintWriter, Mockito.times(1)).close();
Your construction of the PrintWriter doesn't match the mock. You told PowerMockito to return your mock like this:
PowerMockito.whenNew(PrintWriter.class).withArguments(IMAGE_FILE , StandardCharsets.UTF_8.name()).thenReturn(mockPrintWriter);
So you would have to say:
new PrintWriter(IMAGE_FILE, "UTF-8"); // 2 arguments
But instead in your execute method in the code that is being tested, you do:
PrintWriter writer = new PrintWriter(newImageDataTextFile); // only 1 argument
So you either need to change the PowerMockito withArguments clause, or you need to add "UTF-8" to the constructor invocation in the execute method.
This is my code:
public void analyze(String url) throws SiteBusinessException {
Document doc = null;
Response response = null;
try {
response = Jsoup.connect(url).execute();
doc = Jsoup.connect(url).get();
} catch (IOException e) {
LOGGER.warn("Cannot analyze site [url={}, statusCode={}, statusMessage={} ]", new Object[] {url, response.statusCode(), response.statusMessage()});
throw new SiteBusinessException(response.statusMessage(), String.valueOf(response.statusCode()));
}
}
How can I test this method using PowerMock? I want to write test to check that when invoke .execute() then throw IOException and it catch then throw SiteBusinessException.
My code of test.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Jsoup.class})
Test(expected = SiteBusinessException.class)
public void shouldThrowIOException() throws Exception {
Connection connection = PowerMockito.mock(Connection.class);
Response response = PowerMockito.mock(Response.class);
PowerMockito.when(connection.execute()).thenReturn(response);
PowerMockito.mockStatic(Jsoup.class);
expect(Jsoup.connect(SITE_URL)).andReturn(connection);
replay(Jsoup.class);
PowerMockito.when(Jsoup.connect(SITE_URL).execute()).thenThrow(new IOException());
AnalyzerService sut = new AnalyzerServiceImpl();
sut.analyzeSite(SITE_URL);
}
I got
java.lang.Exception: Unexpected exception, expected<com.siteraport.exception.SiteBusinessException> but was<java.lang.IllegalStateException>
??
You need to create a static mock of the Jsoup class. Once you have created such a mock in your test case, you can code your expectations using it.
Please see mock static method using PowerMockito documentation.
Here the Testcase using Mockito and PowerMockito:
I was able to mock the execute method using Mockito + Powermockito (you are using both EasyMock and Mockito?) The code in the test case looks as below:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Jsoup.class})
public class MyClassTest {
#Test(expected = SiteBusinessException.class)
public void shouldThrowIOException() throws Exception {
String SITE_URL = "some_url_string";
Connection connection = Mockito.mock(Connection.class);
Mockito.when(connection.execute()).thenThrow(new IOException("test"));
PowerMockito.mockStatic(Jsoup.class);
PowerMockito.when(Jsoup.connect(Mockito.anyString())).
thenReturn(connection);
AnalyzerService sut = new AnalyzerService();
sut.analyze(SITE_URL);
}
}
I dont understand what I may be doing wrong.
I am trying to follow this tutorial to implement a stateful servlet.
My org.apache.xmlrpc.webserver.XmlRpcServlet.properties looks like this:
com.mydbm.MyDBM=com.mydbm.MyDBMImpl
This is MyDBM interface and MyDBMImpl Code:
public interface MyDBM {
public void setUp(DBConnection dbc) throws ClassNotFoundException,
SQLException, JSchException;
public ArrayList<Database> getDatabases() throws SQLException;
.....
}
public class MyDBMImpl implements MyDBM {
private volatile Connection connection;
private volatile Class<?> driverClass;
private volatile Statement statement;
private volatile DBConnection dbConn;
#Override
public void setUp(DBConnection dbc) throws ClassNotFoundException,
SQLException, JSchException {
this.dbConn = dbc;
if (dbConn.SSHEnabled()) {
LOG.info("SSH Requested, enabling...");
setUpSshConnection();
LOG.info("SSH enabled!");
} else {
LOG.info("SSH not Requested, skipping...");
}
setupConnection();
}
.....
#Override
public ArrayList<Database> getDatabases() throws SQLException {
switch (dbConn.getDatabaseType()) { // NULL POINTER EXCEPTION HERE,
case MYSQL:
return getDatabasesMySQL();
case MSSQLSERVER:
return getDatabasesMSServerSQL();
case SQLITE:
return getDatabasesSQLite();
case SYBASE:
return getDatabasesSyBase();
case POSTGRESQL:
return getDatabasesPostgreSQL();
case ORACLE:
return getDatabasesOracle();
}
return null;
}
The following is my Handler Code:
public class MyDBMXmlRpcRequestProcessorFactoryFactory implements
RequestProcessorFactoryFactory {
private final RequestProcessorFactory factory = new MyDBMRequestProcessorFactory();
private final MyDBM dbm;
public MyDBMXmlRpcRequestProcessorFactoryFactory(
MyDBM dbm) {
this.dbm = dbm;
}
#Override
public RequestProcessorFactory getRequestProcessorFactory(Class aClass)
throws XmlRpcException {
return factory;
}
private class MyDBMRequestProcessorFactory implements
RequestProcessorFactory {
#Override
public Object getRequestProcessor(XmlRpcRequest xmlRpcRequest)
throws XmlRpcException {
return dbm;
}
}
}
This is my Server Code:
public class Server {
private static final int port = 8080;
public static void main(String[] args) throws Exception {
WebServer webServer = new WebServer(port);
XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer();
PropertyHandlerMapping phm = new PropertyHandlerMapping();
MyDBM dbm = new MyDBMImpl();
phm.setRequestProcessorFactoryFactory(new MyDBMXmlRpcRequestProcessorFactoryFactory(
dbm));
phm.setVoidMethodEnabled(true);
phm.addHandler(MyDBM.class.getName(),
MyDBM.class);
xmlRpcServer.setHandlerMapping(phm);
XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer
.getConfig();
serverConfig.setEnabledForExtensions(true);
serverConfig.setContentLengthOptional(false);
webServer.start();
}
}
web.xml content extract:
<web-app>
<!-- SOME PARTS LEFT OUT ->
<servlet>
<servlet-name>XmlRpcServlet</servlet-name>
<servlet-class>org.apache.xmlrpc.webserver.XmlRpcServlet</servlet-class>
<init-param>
<param-name>enabledForExtensions</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>XmlRpcServlet</servlet-name>
<url-pattern>/xmlrpc</url-pattern>
</servlet-mapping>
</web-app>
And finally here is my Client Code:
public class MyClient {
public static void main(String[] args) throws Exception {
// create configuration
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://127.0.0.1:8080/xmlrpctest/xmlrpc"));
config.setEnabledForExtensions(true);
config.setConnectionTimeout(60 * 1000);
config.setReplyTimeout(60 * 1000);
XmlRpcClient client = new XmlRpcClient();
// use Commons HttpClient as transport
client.setTransportFactory(
new XmlRpcCommonsTransportFactory(client));
// set configuration
client.setConfig(config);
// make a call using dynamic proxy
ClientFactory factory = new ClientFactory(client);
MyDBM dbm = (MyDBM)factory.newInstance(MyDBM.class);
try {
dbm.setUp(new DBConnection("myconn", DatabaseType.MYSQL, "localhost", 0, "userid", "secret", "mysql")); // Connection is setup correctly
ArrayList<Database> dbs = dbm.getDatabases(); // Fails with NullPointerException
} catch(Exception e) {
System.out.println(e.getCause().getMessage());
e.printStackTrace();
}
}
}
Error log:
Sep 27, 2013 11:49:26 AM com.myapp.MyDBMImpl setUp
INFO: SSH not Requested, skipping...
jdbc:mysql://localhost/mysql?user=userid&password=secret
Sep 27, 2013 11:49:26 AM org.apache.xmlrpc.server.XmlRpcErrorLogger log
SEVERE: Failed to invoke method getDatabases in class com.myapp.MyDBMImpl: null
org.apache.xmlrpc.common.XmlRpcInvocationException: Failed to invoke method getDatabases in class com.myapp.MyDBMImpl: null
The dbm.setUp(..) method executes correctly and creates the dbConn object on the server. However, when I ran the dbm.getDatabases(), I get a null pointer exception and I believe its because the server creates a new instance of the handler instead of using the initial state of the handler. I am not expert in this but from this tutorial, I was expecting that once dbConn has been set-up, I would be able to use it in subsequent invocations .
Someone kindly assist,I have no Idea where I may be going wrong as I followed the instructions from the tutorial to the latter.
I have tried to past as much information and I would be willing to add more if necessary.
Thank you.
Yes your problem is, that the server creates a new instance every time.
I ran into the same problem and solved it with an extra handler class in which i invoked the instance of the earlier handler.
Something like this:
public class MyDBMXmlRpcRequestProcessorFactoryFactoryWrapper
{
private MyDBMXmlRpcRequestProcessorFactoryFactoryWrapper attribute;
public MyDBMXmlRpcRequestProcessorFactoryFactoryWrapper(class param)
{
attribute = MyDBMXmlRpcRequestProcessorFactoryFactory(param);
}
}