3

I'm using Spring 4.3.8.RELEASE with JUnit 4.12 and Mockito 1.10.18. I have a service that publishes events ...

@Service("organizationService")
@Transactional
public class OrganizationServiceImpl implements OrganizationService, ApplicationEventPublisherAware

            publisher.publishEvent(new ZincOrganizationEvent(id));

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) 
    {
        this.publisher = publisher;
    }

    ...
    @Override
    public void save(Organization organization)
    {
    ...
    publisher.publishEvent(new ThirdPartyEvent(organization.getId()));

My question is, how do I verify in a JUnit test that an event has actually been published?

@Test
public void testUpdate()
{

m_orgSvc.save(org);
// Want to verify event publishing here
Dave
  • 15,639
  • 133
  • 442
  • 830

2 Answers2

6

I prefer the opposite approach, which is more integration test-ey:

  • ‍♂️Mock the ApplicationListener using Mockito
  • Register the mock application listener onto the ConfigurableApplicationContext
  • Do work
  • ✔Verify the mock has received the event

With this approach you are testing that an event has been published by means that someone is receiving it.

Here is the code of a rudimental authentication test. Among other conditions, I test that a login event has occurred

@Test
public void testX509Authentication() throws Exception
{
    ApplicationListener<UserLoginEvent> loginListener = mock(ApplicationListener.class);
    configurableApplicationContext.addApplicationListener(loginListener);

    getMockMvc().perform(get("/").with(x509(getDemoCrt())))//
                .andExpect(status().is3xxRedirection())//
                .andExpect(redirectedUrlPattern("/secure/**"));

    getErrorCollector().checkSucceeds(() -> {
        verify(loginListener, atLeastOnce()).onApplicationEvent(any(UserLoginEvent.class));
        return null;
    });
}

My advice is to unleash the power of Mockito to deeply verify the event arguments. In my case, I will extend my code to:

  • Check that the username in the login event matches the authenticated principal
  • Perform additional tests where the user blatantly fails to login and I will expect one of the various login failure events
usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305
  • For most context configurations, the same approach can be used with an `@Autowired ApplicationEventMulticaster`, with the added bonus of being able to deregister the listener in `@TearDown` - might be useful when Spring context is shared between tests – crizzis Jan 30 '20 at 14:31
3

If you want to test if you didn't forget to call publishEvent method inside your OrganizationServiceImpl you can use something like this:

class OrganizationServiceImplTest {

    private OrganizationServiceImpl organizationService;

    private ApplicationEventPublisher eventPublisher;

    @Before
    public void setUp() {
        eventPublisher = mock(ApplicationEventPublisher.class);

        organizationService = new OrganizationServiceImpl();
        organizationService.setApplicationEventPublisher(eventPublisher)
    }

    @Test
    public void testSave() {

        /* ... */

        organizationService.save(organization);

        verify(eventPublisher).publishEvent(any(ThirdPartyEvent.class));
    }

}

Test case above will verify whether or not there was an invocation of publishEvent method.

For more check the documentation.

Regarding:

My question is, how do I verify in a JUnit test that an event has actually been published?

You have to test ApplicationEventPublisher implementation and probably without mocks if you want to verify actual sending.

ledniov
  • 2,302
  • 3
  • 21
  • 27
  • DId you mean to write "ApplicationEventPublisher eventPublisher" in the @Before method? That amkes the mock instance localized to the before method so in the test the method field "eventPublisher" is still null, unless I"m misreading something. – Dave Jun 28 '17 at 18:52