首页 > 解决方案 > @TransactionalEventListener 注释的方法未在 @Transactional 测试中调用

问题描述

我正在尝试按照以下帖子中提到的示例从实体实现域事件发布:

@DomainEvents 和 @AfterDomainEventsPublication 的示例

但是,我还没有设法让 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 服务并通过 Postman 调用时有效

标签: spring-data-jpadomain-events

解决方案


知道了。由于我的测试使用@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));
    }
}

推荐阅读