Assertion error while running test with LatchCountDownAndCallRealMethodAnswer from RabbitMQ - junit

I have a listener test, where i post a message in a parallel thread and check with LatchCountDownAndCallRealMethodAnswer if the all were processed successfully. Running the test alone, it works perfectly, however if you run all other tests together, it fails because it failed to leave the counter at zero, but the listener received and processed the message normally. Does anyone have any ideas?
My Test Class
#RunWith(SpringRunner.class)
#SpringBootTest
#RabbitListenerTest
#ActiveProfiles("test")
public class EventListenerTest {
EventListener eventListener;
#Autowired
protected RabbitListenerTestHarness harness;
#Autowired
private EventStoreRepository repository;
#SpyBean
private DomainEventPublisher publisher;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
DomainRegister.setDomainEventPublisher(publisher);
eventListener = this.harness.getSpy("eventListenerId");
}
#Test
public void storeEventsListenerTest() throws Exception {
LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
doAnswer(answer).when(eventListener).storeEvents(any(BalanceReserved.class));
publisher.publish(new BalanceReserved("12233", 150.0, BigDecimal.ZERO), "");
assertTrue(answer.getLatch().await(10, TimeUnit.SECONDS));
verify(eventListener, times(1)).storeEvents(any(BalanceReserved.class));
}
#After
public void tearDown() {
DomainRegister.setDomainEventPublisher(null);
reset(eventListener);
repository.deleteAll();
}
}
Error
java.lang.AssertionError

If you have other tests using the same queue, you need to shut down the application context for each test so the test's listeners are stopped. By default, the Spring Test framework caches the application context for reuse. This will cause other tests to "steal" messages.
Add #DirtiesContext to each test class that uses #RabbitListeners, to tell the test framework to shutdown the context.

Related

Why is my Spring Batch Task launching with the same JOB_INSTANCE_ID for multiple job executions?

I have a Spring Batch Task running on our cloud platform that will launch with the provided command line parameters, and then skip over the execution of the first Step with the following error:
[OUT] The job execution id 992 was run within the task execution 1325
[OUT] Step already complete or not restartable, so no action to execute:
StepExecution: id=1071, version=3, name=OFileStep, status=COMPLETED, exitStatus=COMPLETED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0,
writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
I have investigated the metadata tables in the MySQL instance that Spring Batch uses to find that the JOB_INSTANCE_ID is the same between multiple executions, when it should increment by 1 each time.
The #Bean that I have defined for the Job Configuration is:
#Bean
public Job job() {
return jobBuilderFactory.get(OTaskConstants.JOB_NAME)
.listener(listener())
.incrementer(new RunIdIncrementer())
.start(dataTransferTaskStep())
.next(controlMTaskStep())
.build();
}
Is anyone aware of what could be causing this behavior?
Below line clearly says it all.
Step already complete or not restartable, so no action to execute:
Meaning the step/job already complete and can not be restarted. This is the behavior of Spring Batch. In order to by pass this we need to pass an unique argument.
In your case i see you already have RunIdIncrementer. Now question is why it is not working.
Can you see BATCH_JOB_PARMS table to see what arguments are getting passed to the job? May be you are missing something.
You can also use SimpleIncrementor. See below code for explanation.
https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html#JobParametersIncrementer
Remove #Bean annotation on Job.
It causes the Job to be launched with no parameters every time you launch/start application as spring tries to load the bean and which in-turn launches the batch job.
Remove the annotation and use spring scheduler to schedule the jobs.
I had the same issue. Below code helped me resolve it. By adding params in job launcher a new job_instance_id is created for every run.
#SpringBootApplication
public class App implements CommandLineRunner {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job job;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Override
public void run(String... args) throws Exception {
JobParameters params = new JobParametersBuilder()
.addString("JobID", String.valueOf(System.currentTimeMillis()))
.toJobParameters();
jobLauncher.run(job, params);
}
}
Solution
Refer error message above “If you want to run this job again, change the parameters.” The formula is JobInstance = JobParameters + Job. If you do not have any parameters for JobParameters, just pass a current time as parameter to create a new JobInstance. For example,
CustomJobLauncher.java
//...
#Component
public class CustomJobLauncher {
#Autowired
JobLauncher jobLauncher;
#Autowired
Job job;
public void run() {
try {
JobParameters jobParameters =
new JobParametersBuilder()
.addLong("time",System.currentTimeMillis()).toJobParameters();
JobExecution execution = jobLauncher.run(job, jobParameters);
System.out.println("Exit Status : " + execution.getStatus());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Source : https://mkyong.com/spring-batch/spring-batch-a-job-instance-already-exists-and-is-complete-for-parameters/

Unable to Mock functions inside static method Powermock

I am writing unit test for the below code using junit and mockito
public class Abc implements Runnable
{
private static ServerSocket server;
private static int port;
public Abc(int cPort)
{
port = cPort;
}
public void run()
{
init();
}
public static void init()
{
try {
server = new ServerSocket(port);
...something...
client.close();
}
}
catch(IOException e)
{
System.out.println("Exception inside init()...");
e.printStackTrace();
}
}
};
Unit test I have written
#RunWith(PowerMockRunner.class)
#PrepareForTest({ServerSocket.class})
public class abcTest {
#Mock (name = "server") //same name as private var.
ServerSocket mockServer;
#InjectMocks
Abc abc;
#Test
public void testInit() throws Exception {
int port = 1880;
Socket mockClient = Mockito.mock(Socket.class);
PowerMockito.whenNew(ServerSocket.class).
withArguments(anyInt()).thenReturn(mockServer);
abc = new Abc(port);
Abc.init();
PowerMockito.verifyNew(ServerSocket.class).withArguments(port);
}
};
But the call always go to original function definition. I am using junit 4.11 with mockito 2.28.2 and powermockito 2.0.2. I'm using java after a long time. Now its feel like kind of new. Please correct me if anything wrong in the code also.
You will need to change your PrepareForTest annotation
to #PrepareForTest({Abc.class}).
From the PowerMockito docu:
This annotation tells PowerMock to prepare certain classes for testing. Classes needed to be defined using this annotation are typically those that needs to be byte-code manipulated
In this case that refers to the class which creates the new instance of ServerSocket. ServerSocket itself is a non-final public class that does not require special handling from PowerMockito (instead Mockito can deal with this class on its own).
You could also change your test to do the following:
#Test
public void testInit() throws Exception {
int port = 1880;
ServerSocket mockServer = Mockito.mock(ServerSocket.class);
PowerMockito.whenNew(ServerSocket.class)
.withArguments(Mockito.anyInt()).thenReturn(mockServer);
Abc.port = port;
Abc.init();
PowerMockito.verifyNew(ServerSocket.class).withArguments(port);
}
(This first point is unrelated to whether the test fails or succeeds)
I do not know why you mix object's and static method behaviour together, but I think you should change that.In the test instead of creatic an ABC object, just could just set the static port variable directly.
Or alternatively change the whole ABC class into an object.
#InjectMocks failed for me as there is no default constructor
(Actually I got an error message in the console when trying to execute your code)
Additonaly you create a new instance of ABC in your test, which would have overwritten the things done by the annotations. Also as server is created during the init call, there is no need to inject a mock for it.
powermockito 2.0.2 actually depends on junit 4.12, so I am not sure what effects downgrading to an older version might have.
Socket mockClient seemed somewhat unrelated to the code your posted, so I removed it from my example in the answer, however as you use a client (I assume that is your Socket) in your code your probably need to do some mocking for that as well and provide the mock to the method accordingly.

Test case for entity manager

Getting a null pointer exception on Mockito.when for the below code line.
when(entityManager.createQuery(any(String.class)).setParameter(any(String.class), any(String.class)).getSingleResult()).thenReturn("2");
Trying to mock entity manager which is declared as
#Mock
private EntityManager entityManager;
Any help to resolve this?
Complete test class
#RunWith(MockitoJUnitRunner.class)
public class ASDAOImplTest {
#InjectMocks
ASDAOImpl asdaoImpl=new ASDAOImpl();
#Mock
private EntityManager entityManager;
#Before
public void setUp()
{
ReflectionTestUtils.setField(asdaoImpl,"capLimit", 1);
}
#Test
#Ignore
public void validateCappingTest()
{
when(entityManager.createQuery(any(String.class)).setParameter(any(String.class), any(String.class)).getSingleResult()).thenReturn("2");
asdaoImpl.validateCapping("2");
}
}
Edit: Ah, spoke to soon. The error is here...
when(entityManager.createQuery(any(String.class)).setParameter(...)
entityManager is a mock. Per default, a mock will return null. So, entityManager.createQuery(...) will return null. Calling setParameter on null is a NPE.
What you need to insert is a query mock...
#Mock
private Query query;
...
// when createQuery is called, return the mocked query object (instead of null)
when(entityManager.createQuery(any(String.class)).thenReturn(query);
// make sure that setParameter returns this query object back (would otherwise also be NPE)
when(query.setParameter(any(String.class), any(String.class)).thenReturn(query);
// And return the desired result from getSingleResult
when(query.getSingleResult()).thenReturn("2");
Old answer:
Hard to say without the complete code, but a guess would be that you are misssing the Mockito initialization (the part that actually creates object for the variables annotated with #Mock). This can be done in at least two ways:
// Run the whole test with the Mockito runner...
#RunWith(MockitoJUnitRunner.class)
public class MyTestClass { ...
or...
// Do the Mockito initialization "manually"
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
Both ways will lead to Mockito creating all the objects where the variables are annotated with #Mock (it also handles #InjectMocks, etc.).
If this doesn't help, you will have to post more of your test class, otherwise probably noone can help.

Clear database at startup while using flyway

In my case, I'm using spring-boot with gradle and added flyway by simply putting compile 'org.flywaydb:flyway-core' to the build.gradle.
For a simulator run, which is in test, I would like to clear the database before each run. I've put a reset script in /src/test/resources/db/migration/V1.0__Reset.sql (with the real init sql-script at /src/main/resources/db/migration/V1.1__Init.sql), but receive a SyntaxException due to the reset script, which doesn't occur when I run it from the MySQL Workbench.
How can I reset or clear the database at startup?
-- UPDATE --
I've tried to use a Spring DataSourceInitializer, but it seems Flyway scripts are executed before the DS init, so it results in Hibernate Syntax error because the tables aren't found.
#Resource
DataSource ds;
#Bean
public DataSourceInitializer dbInit() throws FileNotFoundException, URISyntaxException {
public DataSourceInitializer dbInit() throws FileNotFoundException, URISyntaxException {
DataSourceInitializer re = new DataSourceInitializer();
re.setDataSource(ds);
re.setEnabled(true);
String str = "classpath:sql/V1.0__Reset.sql";
URL url = ResourceUtils.getURL(str);
org.springframework.core.io.Resource resi = new PathResource(url.toURI());
// new org.springframework.core.io.ClassPathResource(str)
re.setDatabasePopulator(new ResourceDatabasePopulator(resi));
return re;
}
Go for Flyway.clean(). It does exactly what you want. No need to write your own reset script.
You can use ApplicationRunner to run just after the startup and inside it do whatever you want with flyway. You'll also probably want to run migrate after clean:
#Component
public class CleanDatabase implements ApplicationRunner {
#Autowired
private Flyway flyway;
#Override
public void run(ApplicationArguments args) throws Exception {
flyway.clean();
flyway.migrate();
}
}

Error in Parameterised test cases in Junit

I am trying to write a parameterized test case in JUnit. My code looks like this:
#RunWith(Parameterized.class)
#PrepareForTest({AR9DirectDebitFileWriterCustomization.class})
public class AR9DirectDebitFileWriterCustomizationTest2 extends AR3BasicUnitTest {
private DirectDebitExtractDetRec mockObj;
private ARApplicationContext mockAppCon;
private AR9DirectDebitFileWriterCustomization spyObj = null;
AccountDBViewData mockdbData;
AccountDBView mockdbView;
SearchInvoicesDBViewData[] mocksearchInvdbviewdatarr = new SearchInvoicesDBViewData[1];
#Before
public void setUp() throws Exception {
AR9DirectDebitFileWriterCustomization ar9Obj = new AR9DirectDebitFileWriterCustomization(mockdbView, mocksearchInvdbviewdatarr, mockdbData);
spyObj = PowerMockito.spy(ar9Obj);
}
public AR9DirectDebitFileWriterCustomizationTest2(DirectDebitExtractDetRec mockObj_from_collection, ARApplicationContext mockAppCon_from_collection) {
this.mockObj = mockObj_from_collection;
this.mockAppCon = mockAppCon_from_collection;
}
#Parameterized.Parameters
public static Collection<Object[]> getparameters() throws ACMException{
return Arrays.asList(new Object[][]{
{mock(DirectDebitExtractDetRec.class),mock(ARApplicationContext.class)}
});
}
#Test
#Parameters
public final void testAddFileRecordCustObjectARApplicationContext( ) throws Exception {
.....SOME CODE
}
Whenever I right click on the testAddFileRecordCustObjectARApplicationContext function and run it as Junit test I get an initialization error :
java.lang.Exception: No tests found matching Method
testAddFileRecordCustObjectARApplicationContext(amdocs.ar.customizationexits.handlers.helpers.AR9DirectDebitFileWriterCustomizationTest2)
from org.junit.internal.requests.ClassRequest#3fa50b at
org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:37)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.(JUnit4TestReference.java:33)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestMethodReference.(JUnit4TestMethodReference.java:25)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:54)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
After looking for several hours on internet about this issue I could not find anything meaningful. In this scenario I am using spy and powerMocktio Functionality as well.I am not sure what is the root of this error .
And interesting thing is when I run it without using Parameterised test ,it works perfectly fine.
I had very similar error:
No tests found matching data with any parameter from...
According to my observations, it is caused by another strange error:
Unable to mock class ... due to a missing dependency
Only the first I see when I run the test, and the second, when I debug it. According to https://stackoverflow.com/a/23788935/715269,
https://stackoverflow.com/a/25659518/715269, it is the bug connected to classpath reading. The problem disappears, when we upgrade JMockit to higher versions.