@TransactionalEventListener 注释方法未在@Transactional 测试中调用

@TransactionalEventListener annotated method not invoked in @Transactional test

我正在尝试按照下面 post 中提到的示例从实体发布领域事件:

但是我还没有设法 Spring 调用我的带有 @TransactionalEventListener 注释的方法。

参见下面的实体、服务、事件侦听器和测试代码:

@Entity
public class Book extends AbstractAggregateRoot<Book>
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(unique = true)
    private String isbn;

    @Column
    private String name;

    public Book(String isbn, String name)
    {
        this.isbn = isbn;
        this.name = name;
    }

    public void purchase()
    {
        registerEvent(new BookPurchasedEvent(id));
    }

    // getters omitted for brevity
}

服务:

@Service
@Transactional
public class BookService
{
    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository)
    {
        this.bookRepository = bookRepository;
    }

    public void purchaseBook(Integer bookId)
    {
        Book book = bookRepository.findById(bookId)
                                .orElseThrow(NoSuchElementException::new);

        book.purchase();

        bookRepository.save(book);
    }
}

听众:

@Service
public class EventListener
{
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @TransactionalEventListener
    public void handleEvent(BookPurchasedEvent event)
    {
        logger.info("Received event {}", event);
    }
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class BookEventsTest
{
    @Autowired
    private BookService bookService;

    @Autowired
    private EntityManager entityManager;

    @Test
    public void test()
    {
        Book book = new Book("abcd-efgh", "El Quijote");
        book = entityManager.merge(book);

        bookService.purchaseBook(book.getId());
    }
}

来自侦听器的日志消息未被记录。虽然它在部署为 REST 服务并被调用时有效,例如通过邮递员

知道了。由于我的测试是用@Transactional 注释的,因此包装测试方法的事务将被回滚。因此,不会调用带有 @TransactionalEventListener 注释的方法,因为默认情况下它会在 TransactionPhase.AFTER_COMMIT 阶段触发(除非交易成功,否则我对调用它不感兴趣)。所以测试的工作版本如下所示:

@RunWith(SpringRunner.class)
@SpringBootTest
public class BookEventsTest
{
    @Autowired
    private BookService bookService;

    @Autowired
    private BookRepository bookRepository;

    @MockBean
    private EventListener eventListener;

    private Book book;

    @Before
    public void init() {
        book = bookRepository.save(new Book("abcd-efgh", "El Quijote"));
    }

    @After
    public void clean() {
        bookRepository.deleteAll();
    }

    @Test
    public void testService()
    {
        bookService.purchaseBook(book.getId());

        then(eventListener)
                .should()
                .handleEvent(any(BookPurchasedEvent.class));
    }
}