Testing Spring's @Async void-returning methods

Viewed 3708

I have a little problem with @Async methods that return void (or Unit, I'm writing in Kotlin) in my Spring app.

I don't know why, but when @Async method returns void it just doesn't execute, or at least it doesn't do what it is supposed to. Need to say, that in my async methods, I want to send an email using the JavaMailSender, so no big deal. Here is the method:

@Async
override fun sendEmail(locale: Locale, subject: String, message: String, to: String) {

    val msg = sender.createMimeMessage()
    println("1")
    val helper = MimeMessageHelper(msg, true, "utf-8")
    helper.setFrom("email@example.com")
    helper.setTo(to)
    println("2")
    helper.setSubject(getSubject(locale, null))
    println("3")
    helper.setText(processTemplate(locale, null, null), true)
    println("4")
    sender.send(msg)
    println("5")
}

But an email never comes, there is no exception logged (I am running a testNG test).

When I change the signature of the function to make it return Future<String> and add some dummy return line to the end of the function and then I call service.sendEmail(...).get(), the method's body magically executes and the email arrives.

In my @Configuration class, there is @EnableAsync. I also implemented AsyncConfigurer and provided own executor and exception handler, because I thought it could have been something with my executor bean definition, but nothing helps.

This is driving me crazy, because I just want to silently execute something in the background and it doesn't work. By silently I mean that I don't want to be bothered by exceptions thrown inside.

Do you have any ideas?

Update: So as @pleft advised, I put some prints inside my method. Now, when I run mvn clean test, I can see that 1,2,3 are printed, but not each time. Sometimes just 1 and 2 got printed. I also put print in my AsyncUncaughtExceptionHandler, but that one is not called. It looks like the background thread is being killed too soon or something.

Update 2:

My ServiceConfig:

@Configuration
@EnableScheduling
@EnableAsync
@ComponentScan
class ServiceConfig : ApplicationContextAware, EnvironmentAware, AsyncConfigurer {
    override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
        return AsyncUncaughtExceptionHandler { ex, method, params ->
            println("Exception thrown")
        }
    }

    override fun getAsyncExecutor(): Executor {
        val executor = ThreadPoolTaskExecutor()
        executor.corePoolSize = 2
        executor.maxPoolSize = 2
        executor.setQueueCapacity(500)
        executor.threadNamePrefix = "asyncThread"
        executor.initialize()
        return executor
    }
}
/// other beans definitions..

Maybe it is important, maybe not, but I'm using Thymeleaf in that processTemplate method.

1 Answers
Related