New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dontCatchUncaughtExceptions by default #119

Closed
antkorwin opened this Issue Sep 4, 2018 · 7 comments

Comments

Projects
None yet
2 participants
@antkorwin

antkorwin commented Sep 4, 2018

What do you think about making the dontCatchUncaughtExceptions a setting by default?

I think, sometimes catchs of uncaught exceptions lead to unexpected behaviour in tests,

for example:

@Test
public void testFailed() throws InterruptedException {
    ThreadPoolTaskExecutor taskExecutor = setUpExecutor();
    taskExecutor.execute(new ErrorTestTask());
    ListenableFuture<?> future = taskExecutor.submitListenable(new ErrorTestTask());
    Awaitility.await()
              .atMost(1, TimeUnit.SECONDS)
              .pollInterval(10, TimeUnit.MILLISECONDS)
              .until(future::isDone);
}

@Test
public void testSuccess() throws InterruptedException {
    ThreadPoolTaskExecutor taskExecutor = setUpExecutor();
    taskExecutor.execute(new ErrorTestTask());
    ListenableFuture<?> future = taskExecutor.submitListenable(new ErrorTestTask());
    Thread.sleep(1000);
     Assertions.assertThat(future.isDone()).isTrue();
}

private ThreadPoolTaskExecutor setUpExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
    executor.setMaxPoolSize(1);
    executor.afterPropertiesSet();
    return executor;
}

private static class ErrorTestTask implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        throw new RuntimeException(Thread.currentThread().getName() + " -> Error");
    }
}

When I replaced Thread.sleep on the Awaitility, I got a failed test.

I encountered this problem when I refactored a lot of tests to use the Awaitility instead of the Thread.sleep. It took quite a long time to understand that the problem was in another thread.

@johanhaleby

This comment has been minimized.

Collaborator

johanhaleby commented Sep 5, 2018

Hi,

Thanks for your comment. You're the first person to say this ever in the Awaitility's 8 year history so I'm a bit hesitant on changing this. Especially since many times (I would guess most of the times?) when testing systems that are using multiple threads you want to get the error regardless of which thread threw the exception. You might not know which thread that is causing the error and you just want to fail your test any error. Also this would break backward compatibility which is also negative.

WDYT?

@antkorwin

This comment has been minimized.

antkorwin commented Sep 5, 2018

I am confused by the situation, that a test without using the Awaitility will be successful, but it breaks with the Awaitility. Most likely this is more actual for the process of refactoring old tests than for creating a new test.

I completely agree with the fact that breaking back compatibility is bad.

Maybe it will be reasonable to make an example of usage the dontCatchUncaughtExceptions immediately in the documentation of Awaitility. To create a more attention to this point...

@johanhaleby

This comment has been minimized.

Collaborator

johanhaleby commented Sep 5, 2018

I am confused by the situation, that a test without using the Awaitility will be successful, but it breaks with the Awaitility. Most likely this is more actual for the process of refactoring old tests than for creating a new test.

Guess I'm a bit biased since I always start off by using Awaitility ;) Now I better understand what your concern is. I would still suspect that it's more common that you find the current default to be useful otherwise I think someone should have brought this up before.

Maybe it will be reasonable to make an example of usage the dontCatchUncaughtExceptions immediately in the documentation of Awaitility. To create a more attention to this point...

I'm all for improving the docs! If you have an example of what you would like to see I can add it to the docs.

@antkorwin

This comment has been minimized.

antkorwin commented Sep 8, 2018

I tried to make a commit in the documentation, but I didn't found a way to make a PR in your github wiki.

I put my thoughts here:


Exception handling

By default Awaitility catches uncaught throwables in all threads and propagates them to the awaiting thread. This means that your test-case will indicate failure even if it's not the test-thread that threw the uncaught exception.

You can choose to ignore certain exceptions or throwables, see here.

If you don't need to catch exceptions in all threads you can use dontcatchuncaught(link to example 12.).


Example 12 - Ignoring uncaught exceptions:

If you migrate your code from using the Thread.sleep to the Awaitility, then in some cases you can get unexpected behavior if your system throws something in other threads, because the Awaitility handles all uncaught exceptions by default. And if you early used Thread.sleep then most likely you didn't catched exceptions from other threads.

You can turn off it by using thedontCatchUncaughtExceptions option:

    @Test
    public void dontCatchUncaughtExample() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(1);
        executor.afterPropertiesSet();

        executor.execute(new ErrorTestTask());
        ListenableFuture<?> future = executor.submitListenable(new ErrorTestTask());

        Awaitility.await()
                  .dontCatchUncaughtExceptions()
                  .atMost(1, TimeUnit.SECONDS)
                  .pollInterval(10, TimeUnit.MILLISECONDS)
                  .until(future::isDone);
    }

    private static class ErrorTestTask implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            throw new RuntimeException(Thread.currentThread().getName() + " -> Error");
        }
    }
@johanhaleby

This comment has been minimized.

Collaborator

johanhaleby commented Sep 9, 2018

I made some changes (hope you don't mind) and added it to the docs now. Have a look and tell me what you think.

@antkorwin

This comment has been minimized.

antkorwin commented Sep 9, 2018

Thanks for your work!
Now this case is clearly described in the documentation.
You can close this issue.

You made a very useful utility, good luck with your projects!

@johanhaleby

This comment has been minimized.

Collaborator

johanhaleby commented Sep 10, 2018

Thank you for the kind words and for the contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment