How to mock run() method of child thread using mockito-inline - java

Code explanation:
MainThread creates ChildThread based on the list of users - one Childthread per user. I am trying to write a unit test case for MainThread and I want to skip the implementation of ChildThread (a separate unit test case will be written for ChildThread). Below is the code snippet.
#Slf4j
public class MainThread implements Runnable {
private static final UserService USER_SERVICE = ApplicationContextUtils.getApplicationContext().getBean("userService", UserService.class);
private final String threadName;
public MainThread(String threadName) {
this.threadName = threadName;
}
public void run() {
log.info("{} thread created at {}", threadName, LocalDateTime.now());
List<UsersDTO> usersDTOs = USER_SERVICE.getUsers();
ExecutorService executor = Executors.newFixedThreadPool(usersDTOs.size());
usersDTOs.stream().map(ChildThread::new).forEach(executor::execute);
executor.shutdown();
}
}
#Slf4j
public class ChildThread implements Runnable {
private final UserDTO userDTO;
public ChildThread(UserDTO userDTO) {
this.userDTO = userDTO;
}
public void run() {
log.info("Child thread created for user: {}", userDTO.getName());
// some business logic
}
}
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MainThreadTest {
#Mock
private ApplicationContext applicationContext;
#Mock
private UserService userService;
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
new ApplicationContextUtils().setApplicationContext(applicationContext);
}
#Test
void test() {
Mockito.when(applicationContext.getBean("userService", UserService.class)).thenReturn(userService);
Mockito.when(userService.getUsers()).thenReturn(MockObjectHelper.getUsersList());
ChildThread childThread = new ChildThread(MockObjectHelper.getUser());
ChildThread spy = spy(childThread);
doNothing().when(spy).run();
MainThread mainThread = new MainThread("TestingThread");
mainThread.run();
verify(userService, times(1)).getUsers(any());
}
}
Despite spying ChildThread, the run() method of ChildThread is executed. doNothing().when(spy).run(); is of no effect. For some reason, I cannot use PowerMockito. How to achieve this with mockito-inline (version 3.10.0) and java8?
Any help would be appreciated.

Instead of mocking ChildThread refactor MainThread so ExecutorService is injected in constructor.
Then mock ExecutorService and check if it is receiving correct ChildThread instances.

Related

How to unit test asynchronous code made synchronous using CountdownLatch

Below is the class I want to test :
SomeClass.java
public void SomeClass {
final CountDownLatch latch = new CountDownLatch(1);
int result;
registerCallbackWithService(new MyCallback());
public int callToExternalService(){
//Do some stuff and make service call
latch.await();
return result;
}
class MyCallback implements ServiceCallback {
#Override
public void onResult(final int res) {
//do something
result = res;
latch.countdown();
}
}
}
The callback MyCallback was registered earlier before invoking callToExternalService().
If I write a simple test to just mock the service call made in callToExternalService(), the test keeps on running infinitely because of latch.await().
How can I test the logic in callToExternalService() as well as in onResult() ?
I modified by code to expose the callback that I am registering using a package-protected function as below :
public void SomeClass {
private final CountDownLatch latch = new CountDownLatch(1);
private int result;
registerCallback(new MyCallback());
public int callToExternalService(){
//Do some stuff and make service call
latch.await();
return result;
}
private class MyCallback implements ServiceCallback {
#Override
public void onResult(final int res) {
//do something
result = res;
latch.countdown();
}
}
protected registerCallback(ServiceCallback callback) {
registerCallbackWithService(callback);
}
}
Now, for testing I do my testing by creating a new class SomeClassTest extends SomeClass and do my testing using an instance of this class. In SomeClassTest all I do is override registerCallback() to access the callback instance that is being registered.
public class ServiceTest {
private ServiceCallback mServiceCallback;
class SomeClassTest extends SomeClass {
#Override
registerCallback(ServiceCallback callback) {
mServiceCallback = callback;
super.registerCallback(callback);
}
}
}
Now all I have to do it using doAnswer, invoke the callback upon service request which results in the execution of latch.countdown() on the same latch reference that is put on await just after making the service request.
SomeClassTest someClassInstance = new SomeClassTest();
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
mServiceVCallback.onResult(int_I_want_to_test_for)
return null;
}
}).when(someClassInstance).service_request_before_latch_await();
int response = someClassInstance.callToExternalService();
assertEquals(response, expected_response);

Execution Exception : While calling method from Callable

I am trying to run following but I am facing null pointer at userService.getName();
without thread it is working fine but with thread it is throwing null pointer exception
public class Sheduler implements Callable{
private UserService userService;
private SchedulerExecuterService scheduler=Executers.newScheduledThreadpool(2);
#Inject
public Sheduler (UserService userService){
this.userService = userService;
}
Future future = scheduler.submit(new Callable(){
#override
public String call() throws Exception{
**return userService.getName();//NUll pointer Exception**
}
)}
public void startpool(){
final Runnable beeper = new Runnable(){
public void run(){
UserService userService = new UserService();
Sheduler scd=new Sheduler (UserService userService);
future.get();
}
}
scheduleAtFixedRate(beeper,5,5,TimeUnit.SECONDS);
}
public static void main(){
Sheduler scduler=new Sheduler();
scduler.startpool();
}
You use an #Injecton your Scheduler constructor but still instantiate it manually. Your UserService is never injected and therefore is null throwing the NullPointerException.

Unit test for Runnable with Mockito

I have a code like this for which I would like to write unit test.
public class TestClass {
private final Executor executor;
private final Handler handler;
TestClass(Executor executor, Handler handler) {
this.executor = executor;
this.handler = handler;
}
void doSomething(String param1) {
executor.execute(new Runnable() {
#Override
public void run() {
//do something
handler.callHandler();
}
});
}
}
How can I use Mockito / Powermockito to verify if the callHandler() method is invoked.
Pass a mock Handler to the constructor of TestClass.
Then use Mockito.verify() to assert that callHandler() method was called.
Involving concurrency
You can stub an answer that counts down on a CountDownLatch to make the test wait for the handler to be hit. Waiting will involve setting a reasonable timeout, which can be tricky, you don't want it too high, or failure will make the test run much longer, and not too low so that you don't get false positives.
Handler handler = mock(Handler.class);
CountDownLatch finished = new CountDownLatch(1);
doAnswer(invocation -> {
finished.countDown();
return null;
}).when(handler).callHandler();
TestClass testClass = new TestClass(executor, handler);
testClass.doSomething("thisThing");
boolean ended = finished.await(10, TimeUnit.SECONDS);
assertThat(ended).isTrue();
verify(handler).callHandler();
Bypassing concurrency
If you're only trying to determine whether the handler is invoked you can use an Executor that executes on the same thread. This will make for a more stable test.
Handler handler = mock(Handler.class);
Executor executor = new Executor() {
#Override
public void execute(Runnable command) {
command.run();
}
};
TestClass testClass = new TestClass(executor, handler);
testClass.doSomething("thisThing");
verify(handler).callHandler();
Another way you can handle the concurrency issue is to mock the Executor to "do nothing" when called and use an ArgumentCaptor in your test to capture the Runnable it would have invoked. Once you have the Runnable you can manually invoke it in the same thread as your test.
Here's an example:
#Mock
private Executor executor;
#Mock
private Handler handler;
#Before
public void setup() {
openMocks(this);
doNothing().when(executor).execute(any());
}
#Test
public void runTest() {
TestClass testClass = new TestClass(executor, handler);
testClass.doSomething("the thing");
ArgumentCaptor<Runnable> runnable = ArgumentCaptor.forClass(Runnable.class);
verify(executor).execute(runnable.capture());
Runnable capturedRunnable = runnable.getValue();
capturedRunnable.run();
verify(handler).callHandler();
}

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 test method that starts new thread which may fail due to operating system

I have an application built upon Spring Boot. There is simple controller with a method which creates new Thread and starts it. However a runnable executes unix command (nc) (used ProcessBuilder for that). Thus when I'm runnning it on the windows I get exceptions from started thread. Indeed it can not run unix program. Now I would like to write a test for this controller, but I'm wondering is it possible and reasonable. I was thinking about changing behaviour of runnable task just for testing, although I don't know how can it be done. Thanks for any help and other ideas/solutions for this case.
Controller:
#Controller
public class TaskController {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(task-%d").build();
#RequestMapping(value = "/startTask")
public #ResponseBody ResponseEntity<String> startTask() {
Runnable runnable= new Task();
threadFactory.newThread(runnable).start();
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
Task:
public class Task implements Runnable {
#Override
public void run() {
// start unix process
}
}
Application class:
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Integration Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest("server.port=0")
#DirtiesContext
public class ApplicationTest {
#Value("${local.server.port}")
private int port;
#Test
public void shouldStartTask() throws Exception {
// when
ResponseEntity<String> entity = new TestRestTemplate().getForEntity("http://localhost:" + this.port + "/startTask", String.class);
// then
assertThat(entity.getStatusCode()).isSameAs(HttpStatus.ACCEPTED);
}
}
You might find it easier to test your program if you the extract the processing logic of your application (which does things using threads) from your controller logic, placing the processing logic in a separate service layer, which your controller delegates to. Design the service layer to have an API that is easy to unit test, by providing methods for accessing its current state, not just for performing actions. Use dependency injection to connect your controller to your service layer.
So, something like this:
public interface Service
{
// Sets this.wasTaskStarted() == true
void startTask();
boolean wasTaskStarted();
void awaitCompletionOfTask();
}
#Controller
public class TaskController {
private final Service service;
#Autowired
public TaskController(Service service) {
this.service = service;
}
#RequestMapping(value = "/startTask")
public #ResponseBody ResponseEntity<String> startTask() {
service.startTask();
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
public ServiceImpl implements Service {
private final ThreadFactor threadFactory = new ....;
private Thread taskTread;
#Override
public synchronized void startTask() {
if (taskTread == null) {
taskTread = threadFactory.newThread(new Task());
taskTread.start();
notifyAll();
}
// else already started
}
#Override
public synchronized boolean wasTaskStarted() {
return taskTread != null;
}
#Override
public synchronized void awaitCompletionOfTask() {
while (taskTread == null) {
wait();
}
taskTread.join();
}
}
To test that your controller starts a task, you just need to test that Service.wasTaskStarted() is true after calling TaskController.startTask().
You also have to test your service layer:
public class ServiceImplTest
{
#Test
public void testStartTask() {
final ServiceImpl service = new ServiceImpl(....);
service.startTask();
assert(service.wasTastStarted());
}
#Test
public void testRunTask() {
final ServiceImpl service = new ServiceImpl(....);
service.startTask();
service.awaitCompletionOfTask();
// Add assertions here to test that the task did what it ought to do
}
}
Thanks for the suggestion. You just opened my mind and I changed the design a bit. I resigned from an integration test. From business point of view, I don't need to check whether task has been started or even completed. Now it looks as follows:
Controller:
#Controller
public class TaskController {
private ThreadService threadService;
#Autowired
public TaskController (ThreadService threadService) {
this.threadService= threadService;
}
#RequestMapping(value = "/startTask")
public #ResponseBody ResponseEntity<String> startTask() {
// some conditions here which I would like to test
threadService.startNewThread(new Task());
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
Task:
public class Task implements Runnable {
#Override
public void run() {
// start unix process
}
}
Thread service:
#Component
public class ThreadService {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("task-%d").build();
public void startNewThread(Runnnable task) {
threadFactory.newThread(task).start();
}
}
And I decided to unit test my controller, stubbing ThreadService with mockito:
#RunWith(MockitoJUnitRunner.class)
public class TaskControllerTest {
#Mock
ThreadService threadService;
#InjectMocks
private TaskController objectUnderTest;
#Test
public void shouldStartTask() throws FileNotFoundException {
// when
ResponseEntity<String> response = objectUnderTest.startTask();
// then
assertThat(response.getStatusCode()).isSameAs(HttpStatus.ACCEPTED);
// more assertions
}

Categories