1

I created an EventEmitter that passes the events to the listeners in a new thread:

public class EventEmitter{

private Map<String,List<EventListener>> listeners = new HashMap<String,List<EventListener>>();
private Executor executor = Executors.newCachedThreadPool();

protected EventEmitter(Executor executor){
    this.executor = executor;
}

public void publish(Event e) {
    listeners.get(e.getClass().toString()).stream().forEach(listener->{
        executor.execute(()->{listener.beNotifiedOfEvent(e);});
    });
}

public void subscribe(EventListener listener,Class<?> eventClass) {

    assert Arrays.asList(eventClass.getInterfaces()).contains(Event.class);

    List<EventListener> listenersThatListenToIt = listeners.get(eventClass.toString());

    if(listenersThatListenToIt!=null){
        listenersThatListenToIt.add(listener);
    } else {
        listenersThatListenToIt = new ArrayList<EventListener>();
        listenersThatListenToIt.add(listener);
        listeners.put(eventClass.toString(),listenersThatListenToIt);
    }

}
}

I also print a message in the listener after two seconds when an event is received:

@Component
public class StudentRegisteredEventListener implements EventListener {

@Override
public void beNotifiedOfEvent(Event e) {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    StudentRegisteredEvent studentRegisteredEvent = (StudentRegisteredEvent) e;
    System.out.println("Received student registered event! Student's name is: "+studentRegisteredEvent.getRegisteredStudent());
}

}

I have the following JUnit test:

    @Test
public void test_emitter_notifies_listener() {
    //given
    EventEmitter emitter = new CachedThreadPoolEventEmitter();
    //StudentRegisteredEvent event = new StudentRegisteredEvent();

    EventListener listener = Mockito.mock(StudentRegisteredEventListener.class);

    Student registeredStudent = new Student();
    registeredStudent.setName("studentName");

    //when
    emitter.subscribe(listener,StudentRegisteredEvent.class);
    emitter.publish(new StudentRegisteredEvent(registeredStudent));

    //then

    System.out.println("end of test");
}

My problem is that when the parent thread ends before the end of the "beNotifiedOfEvent" call, the line from the listener method is not printed, so I only see "end of test" on the console.

What I'm interested in is: what is the reason for this behaviour, and whether or not this will work outside of the context of a test. I plan to publish the event from a Spring MVC controller. Is this guaranteed to complete after the the parent thread is done with the execution?

Tyler Durden
  • 2,031
  • 4
  • 20
  • 25

2 Answers2

2

It's not the thread terminating but the whole process. JUnit kills the process when the test finishes, so not even non-daemon threads can keep a JUnit testing process alive. (It is totally straightforward if you think about it, noone wants a test process running after it finished actual testing). In Java, threads do not have a hierarchy, there is no such thing as a 'parent' thread. However nearly every application has some kind of 'main' thread. JUnit has its own main thread, runs the tests inside it, and calls System.exit(0); at the end.

If you want to test some timed events, you will have to explicitly wait in the test case for the threads. Additionally, I would recommend to use a ScheduledExecutorService instead of Thread.sleep-s.

Community
  • 1
  • 1
Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97
1

If it does not work within the context of a test, you should not take anyone's word for it that it may work outside the context of a test. Get your test to work correctly first.

If you want your test to complete normally, then you have to wait for all threads that it spawned to end. Assuming your Executor is actually an ExecutorService, this is done as follows:

executorService.shutdown();
try 
{
    executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
}
catch (InterruptedException e) 
{
    ...
}

While the above code is executing, your task should run to completion, so you should see the message displayed right before your task ends.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142