Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Spring @EventListener method?

I have some event publishing:

@Autowired private final ApplicationEventPublisher publisher;
...
publisher.publishEvent(new MyApplicationEvent(mySource));

I have this event listener:

class MyApplicationEventHandler {

    @Autowired SomeDependency someDependency;

    @EventListener public void processEvent(final MyApplicationEvent event) {
        // handle event...
    }
}

I need to test it using EasyMock. Is there a simple way to publish something in test and assert that my event listener did something?

EDIT:

I tried to create mock test like this:

// testing class
SomeDependency someDependency = mock(SomeDependency.class);

MyApplicationEventHandler tested = new MyApplicationEventHandler(someDependency);

@Autowired private final ApplicationEventPublisher publisher;

@Test
public void test() {
    someDependency.doSomething(anyObject(SomeClass.class));
    replay();
    publisher.publishEvent(new MyApplicationEvent(createMySource()));
}

It didn't work.

java.lang.AssertionError: 
  Expectation failure on verify:
    SomeDependency.doSomething(<any>): expected: 1, actual: 0
like image 638
JiKra Avatar asked Jun 20 '18 14:06

JiKra


People also ask

How do you test spring Microservices?

Spring Boot microservices can be tested by using the JUnit testing framework, by annotating the test with @Test . Alternatively, to only load slices of functionality, use the @SpringBootTest annotation while listing the Spring components that participate in the test scenario in the annotation declaration.

How do you test an event listener?

You can test the event listener does its thing by instantiating the listener, then getting it to handle your event. // Create a listener instance. $listener = app()->make(YourListener::class); // or just app(YourListener::class) // Create an event instance.

How do you run a test case in spring?

Then, configure the Application context for the tests. The @Profile(“test”) annotation is used to configure the class when the Test cases are running. Now, you can write a Unit Test case for Order Service under the src/test/resources package. The complete code for build configuration file is given below.


2 Answers

First, As you're using Spring Boot, the testing of these becomes pretty straightforward. This test will spin up the boot context and inject a real instance of ApplicationEventPublisher, but create a mocked instance of SomeDependency. The test publishes the desired event, and verifies that your mock was invoked as you expected.

@RunWith(SpringRunner.class)
@SpringBootTest
public class EventPublisherTest {

   @Autowired 
   private final ApplicationEventPublisher publisher;

   @MockBean
   private SomeDependency someDependency;

   @Test
   public void test() {
      publisher.publishEvent(new MyApplicationEvent(createMySource()));

      // verify that your method in you 
      verify(someDependency, times(1)).someMethod();
   }
}
like image 159
lane.maxwell Avatar answered Oct 15 '22 00:10

lane.maxwell


In case, spinning up the whole Spring context is not an option, with Spring Boot 2.0.0 ApplicationContextRunner was introduced.

ApplicationContextRunner can create an application context in your test, allowing for more control over the context.

A complete test example could be:

package net.andreaskluth.context.sample;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

public class SimpleEventTest {

  private final ApplicationContextRunner runner = new ApplicationContextRunner();

  @Test
  public void happyPathSuccess() {
    AtomicBoolean sideEffectCausedByEvent = new AtomicBoolean(false);
    ObservableEffect effect = () -> sideEffectCausedByEvent.set(true);

    runner
        .withBean(SomeEventListener.class, effect)
        .run(
            context -> {
              context.publishEvent(new SomeEvent());
              assertThat(sideEffectCausedByEvent.get()).isTrue();
            });
  }

  public interface ObservableEffect {
    void effect();
  }

  @Component
  public static class SomeEventListener {

    private final ObservableEffect effect;

    public SomeEventListener(ObservableEffect effect) {
      this.effect = effect;
    }

    @EventListener(SomeEvent.class)
    public void listen() {
      effect.effect();
    }
  }

  public static class SomeEvent {}
}

like image 44
Andreas Avatar answered Oct 14 '22 23:10

Andreas