Junit Mocking a async thread call - java

I am trying to unit test Class A, where Class A is as follows
Class A{
private final classB = new ClassB();
public Void test(ScheduledEvent scheduledEvent, Context context) {
try {
Thread thread2 = new Thread(() -> doHandleRequest());
thread2.start();
thread2.join(3000);
if (thread2.isAlive()) {
thread2.interrupt();
// sleep for 30 seconds to flush any logs or CFN metrics.
Thread.sleep(30000);
}
} catch (InterruptedException ex) {
}
}
void doHandleRequest() {
try {
classB.sweep();
} catch(Exception ex) {
log.error("Exception in SweepThread while sweeping and deleting lambdas", ex);
}
}
}
Class B{
public void sweep(){
// do something;
}
I want to unit test Class A scenario where thread2.isAlive() condition is true and it goes to that if block.

First, you need to change how you create ClassB object to allow mocking, it can not be done when objects get created every time.
You can do that like this
public class ClassA {
private final ClassB b;
// this will allow you to inject mock from outside, as it's final, can be initialized only via a constructor.
public ClassA(ClassB b){
this.b = b;
}
// your remaining code
}
And now, in your test case, you need to inject mocks.
#Test
public void test() throws InterruptedException {
ClassB classB = Mockito.mock(ClassB.class);
ClassA classA = new ClassA(classB);
MockitoAnnotations.initMocks(this); // this will initialize mocks
//above can be skipped if you are using annotations for the same
Mockito.stubVoid(classB)
.toAnswer(invocationOnMock -> {
Thread.sleep(4000);
return null;
}).on().sweeps();
parent.test(null, null);
}
To make sure, your thread is alive after join, you can add delay in mock method of classB.
As your method returns void, we need to use stubVoid, otherwise it will be like this.
Mockito.when(mock.method(/* args, if, any */)).thenAnswer(mock -> {
Thread.sleep(delayInMilliseconds);
return desiredValue;
})

Related

Using Async multithreading in Spring to run concurrent tasks

I am very new to Spring and I am trying to call two methods from two separate classes a number of times, and I want each invocation to spin a new thread so they run concurrently. This is the code I have:
the main class:
#SpringBootApplication
#EnableOAuth2Client
#EnableAsync
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
class SomeOtherClass {
for (int i = 0; i < 100; i++) {
Class1 class1 = new Class1();
class1.method1(//its arguments);
}
// doing other things
// ...
// method 2 will use the side effects of method 1, so ideally this next
// for loop should start only after the previous one is over
for (int i = 0; i < 50; i++) {
Class2 class2 = new Class2();
class2.method2(//its arguments);
}
}
public Class1 {
#Async("threadPoolTaskExecutor")
public void method1() throws Exception {
LOGGER.info("Running this in {}", Thread.currentThread().getName());
}
}
public Class2 {
#Async("threadPoolTaskExecutor")
public void method2() throws Exception {
LOGGER.info("Running this in {}", Thread.currentThread().getName());
}
}
#Configuration
#EnableAsync
public class ThreadConfig {
#Bean("threadPoolTaskExecutor")
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(100);
executor.initialize();
return executor;
}
}
The problem is when I run the application I only see one thread name printing, which means it's not running in a multithreaded way. Looking at the logs, I can also see the calls are being made sequentially. Having done multithreading using standard Java (Runnable), I know the multithreaded version should finish much faster. Since I am very new to Spring, I do not understand what I am doing wrong.
I have redacted the method names and logic, but the annotations and class structures are exactly thee same, so if you see any problem with that please point that out.
To get method1 and method2 work asynchronously you have to let Spring manage instances of the Class1 and Class2. Replace Class1 class1 = new Class1(); with the dependency injection.
#Service
public Class1 {
...
#Service
class SomeOtherClass {
#Autowired
Class1 class1;
//...
// your loops
EDIT:
If you need to perform second loop only after completion of all async executions in the first loop then you can use Future class:
#Async(...)
public Future<Object> method1() {
...
return null;
}
List<Future<Object>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
futures.add(class1.method1(/*its arguments*/));
}
futures.forEach(f -> {
try {
f.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
});
// all of invocations of method1 are finished

How do I invoke a real function in my unit tests with jMockit?

I have read this post: Is there a way in JMockit to call the original method from a mocked method?
but the recommend solution throws a NPE. Here is my source
static Map<String, Boolean> detectDeadlocks(int timeInSeconds) {
final Map<String, Boolean> deadlockMap = new LinkedHashMap<>();
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// I want to check if the method is run.
**deadlockMap.put("deadlock", isDeadlockAfterPeriod());**
}
}, timeInSeconds * 1000);
return deadlockMap;
}
I want to be able to invoke isDeadlockAfterPeriod in my unit test. This is a static method that I have mocked in my unit tests.
My unit test code
#Test
public void testDetectDeadlocks() throws Exception {
new Expectations(){
{
// Called from inside the TimerTask.
ClassUnderTest.isDeadlockAfterPeriod();
result = false;
}
};
TimerMockUp tmu = new TimerMockUp();
Deadlocks.detectDeadlocks(0);
Assert.assertEquals(1, tmu.scheduleCount);
}
class TimerMockUp extends MockUp<Timer> {
int scheduleCount = 0;
#Mock
public void $init() {}
#Mock
public void schedule(Invocation invocation, TimerTask task, long delay) {
scheduleCount ++;
invocation.proceed(); // Trying to call the real method, but this throws a NPE.
}
}
Error stack trace is seen with JUnit in Eclipse.
java.lang.NullPointerException
at com.myproject.test.DeadlocksTest$TimerMockUp.schedule(DeadlocksTest.java:78)
at com.myproject.test.Deadlocks.detectDeadlocks(Deadlocks.java:41)
at com.myproject.test.DeadlocksTest.testDetectDeadlocks(DeadlocksTest.java:86)
Your problem is that you are also faking the Timer's constructor (and not only the schedule method).
By doing so, you are preventing the correct initialization of the Timer, and as you are then using its real implementation, it fails to do so.
Specifically (with the sources I have), you are preventing the initialization of its queue field, which is used on its mainLoop() method (the one that will call to your TimerTask.run()).
Also, you need to do partial mocking of Deadlocks class, as I understand that isDeadlockAfterPeriod is also a static method for the said class.
I'll leave you here a working example:
Deadlocks.class
public class Deadlocks {
public static Map<String, Boolean> detectDeadlocks(int timeInSeconds) {
final Map<String, Boolean> deadlockMap = new LinkedHashMap<>();
new Timer()// this will be the real constructor
.schedule( // this will be first mocked, then really executed
new TimerTask() {
#Override
public void run() {
deadlockMap.put("deadlock", isDeadlockAfterPeriod()); // this will put false after the mock is called
}
}, timeInSeconds * 1000);
return deadlockMap;
}
public static Boolean isDeadlockAfterPeriod() {
return true; // this, we will mock it
}
}
Test class
#RunWith(JMockit.class)
public TestClass{
#Test
public void testDetectDeadlocks() throws Exception {
new Expectations(Deadlocks.class){ // do partial mocking of the Deadlock class
{
// Called from inside the TimerTask.
Deadlocks.isDeadlockAfterPeriod();
result = false;
}
};
// prepare the fake
TimerMockUp tmu = new TimerMockUp();
// execute the code
Map<String, Boolean> result = Deadlocks.detectDeadlocks(0);
// assert results
assertThat(tmu.scheduleCount, is(1));
assertThat(result.size(), is(1));
assertThat(result.get("deadlock"), is(false));
}
class TimerMockUp extends MockUp<Timer> {
int scheduleCount = 0;
#Mock
public void schedule(Invocation invocation, TimerTask task, long delay) {
scheduleCount ++;
invocation.proceed();
}
}
}
In general, be very careful when faking constructors, as you may leave the instances in an inconsistent state.

How to write unit test which creates new thread

I have following method for test:
public class classToTest{
#Autowired
private Alternator alternator;
public void methodToTest(){
Thread t = new Thread(new Runnable() {
public void run() {
while(true) {
if(alternator.get()) {
System.out.print("Hello");
alternator.set(false);
}
}
}
};
t.start()
}
}
I need to check that was invoked method
alternator.set(false);
How can I do it?
Instead of starting a thread directly, can you pass in an "Executor" instance?
For example...
public class ClassToTest{
#Autowired
private Alternator alternator;
#Autowired #Qualifier("myExecutor")
private java.util.concurrent.Executor executor;
public void methodToTest() {
Runnable runnable = new Runnable() {
public void run() {
while(true) {
if(alternator.get()) {
System.out.print("Hello");
alternator.set(false);
}
}
};
executor.execute(runnable);
}
}
Now you can test this easier...
public class ClassToTestTest {
...
#Before
public void setup() {
alternator = mock(Alternator.class);
executor = mock(Executor.class);
obj = new ClassToTest();
ReflectionTestUtils.setField(obj, "alternator", alternator);
ReflectionTestUtils.setField(obj, "executor", executor);
}
#Test
public void shouldStartRunnable() {
obj.methodToTest();
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(executor).execute(runnableCaptor.capture());
Runnable runnable = runnableCaptor.getValue();
// Now test your actual "runnable"...
when(alternator.get()).thenReturn(true);
runnable.run();
verify(alternator).set(false);
}
}
(Have not tried to compile this, so I apologise if there are any mistakes!!)
Though Bret's post of passing in an executor is very much recommended, you can use the timeout() mock verification setting to test for asynchronous conditions.
verify(alternator, timeout(500)).set(false);
Note that this will necessarily increase the flakiness of your test (i.e. the likelihood that the test fails when the code passes). With a sensible timeout value, that flakiness should be negligible, but if you're making this a part of your core test infrastructure you may consider refactoring to allow for synchronous execution in the test.

How to run method at set time?

Right now, I'm working on a project, and my setup is something like this: I have a class (Foo) which has several methods inside of it which must be activated at different times by the main class.
I've been researching this for quite a while, and the only thing I can find is the Timer class, which doesn't quite work, as it can seemingly only time a class, and I don't want 25 different classes for such a basic program.
How do I activate each method in Foo individually?
The following class works using an extension of TimerTask (MethodTimerTask) which take in input the Foo instance and the method name to call.
In this way, using reflection, the different methods can be called at different times with only one extended TimerTask class.
public class MyTimerTaskExample {
/**
* #param args
*/
public static void main(String[] args) {
Timer timer = new Timer();
Foo foo = new Foo();
timer.schedule(new MethodTimerTask(foo, "method1"), 1000);
timer.schedule(new MethodTimerTask(foo, "method2"), 3000);
}
public static class MethodTimerTask extends TimerTask {
private String methodName;
private Foo fooInstance;
private Exception exception;
public MethodTimerTask(Foo fooInstance, String methodName) {
this.methodName = methodName;
this.fooInstance = fooInstance;
}
#Override
public void run() {
try {
Foo.class.getMethod(methodName).invoke(fooInstance);
} catch (Exception e) {
this.exception = e;
}
}
public Exception getException() {
return exception;
}
public boolean hasException() {
return this.exception != null;
}
}
public static class Foo {
public void method1() {
System.out.println("method1 executed");
}
public void method2() {
System.out.println("method2 executed");
}
}
}
Bonus: the method getException() returns the eventual Exception catched while executing methodName of Foo.

Check that a method is not invoked

I want to check that a method is not run and tried to do it with an Expectation setting times = 0;, however I don't get the expected behaviour.
For example, the following test passes, although the Session#stop method is called, and the expectation has a times = 0; condition:
public static class Session {
public void stop() {}
}
public static class Whatever {
Session s = new Session();
public synchronized void method() {
s.stop();
}
}
#Test
public void testWhatever () throws Exception {
new Expectations(Session.class) {
#Mocked Session s;
{ s.stop(); times = 0; } //Session#stop must not be called
};
final Whatever w = new Whatever();
w.method(); // this method calls Session#stop => the test should fail...
// ... but it passes
}
Note: If I replace the code with { s.stop(); times = 1; }, the test passes too: I must be missing something obvious here...
The reason of the unexpected mocking behavior is that you inadvertently used partial mocking on an strictly mocked type. In this case, recording an expectation with times = <n> means that the first n matching invocations will be mocked, and after that any additional invocations will execute the original "unmocked" method. With regular mocking instead, you would get the expected behavior (ie, an UnexpectedInvocation getting thrown after n invocations).
The proper way to write the test is:
public static class Session { public void stop() {} }
public static class Whatever {
Session s = new Session();
public synchronized void method() { s.stop(); }
}
#Test
public void testWhatever ()
{
new Expectations() {
#Mocked Session s;
{ s.stop(); times = 0; }
};
final Whatever w = new Whatever();
w.method();
}
Alternatively, it can also be written with a verification block instead, which is usually better for situations like these:
#Test
public void testWhatever (#Mocked final Session s)
{
final Whatever w = new Whatever();
w.method();
new Verifications() {{ s.stop(); times = 0; }};
}
Related to this I had trouble with JMockit, times = 0 and the #Tested annotation.
With the #Tested annotation you still have a 'real' class, so when registering an Expectation or a Verification (even with times = 0) on this real class, JMockit tries to execute the method. Solution is to partially mock the class in the Expectations:
#Tested
Session s;
new Expectations(Session.class) {{
s.stop(); times = 0; } //Session#stop must not be called
};
This is the only way I found to use times=0 on methods from #Tested classes.
I found a workaround with the MockUp class - the test below fails as expected - I would still like to understand why the original approach was not working.
#Test
public void testWhatever () throws Exception {
new MockUp<Session>() {
#Mock
public void stop() {
fail("stop should not have been called");
}
};
final Whatever w = new Whatever();
w.method();
}
Try maxTimes instead, you also can reference stop() in a static way:
#Test
public void test(#Mocked Session mockSession){
final Whatever w = new Whatever();
w.method();
new Verifications(){
{
Session.stop();
maxTimes = 0;
}
};
}
From memory, something like
verify( s , times(0) ).stop();
will work. Trouble is, the Session in Whatever isn't your #Mock'ed one, but another object, so insert a
w.s = s;
just before w.method().
Cheers,

Categories