In AssertJ, both
assertThatCode(…).isInstanceOf(…) and assertThatThrownBy(…).isInstanceOf(…) seem to work similarly.
How do they differ, and why would I choose one over the other?
I also noticed that assertThatThrownBy() allows chaining doesNotThrowAnyException().
What’s the proper usage for each?
I had this confusion too.
One practical difference I noticed: assertThatThrownBy() will fail immediately if no exception occurs, whereas assertThatCode() lets you explicitly assert no exception.
So for negative testing (code should not fail), assertThatCode().doesNotThrowAnyException() reads much better.
Example:
// Expecting exception
assertThatThrownBy(() -> service.readFile("missing.txt"))
.isInstanceOf(FileNotFoundException.class);
// Expecting no exception
assertThatCode(() -> service.processFile("valid.txt"))
.doesNotThrowAnyException();
Personally, I use assertThatThrownBy for exception validation and assertThatCode for asserting code is safe. It helps maintain clarity in my test suite.
From my experience, I think of it this way: assertThatThrownBy() is for when you expect an exception, while assertThatCode() is for when you mostly don’t expect one but still want to assert.
-
assertThatThrownBy(() -> …) is great when testing error scenarios; you can check the type, message, and even cause.
-
assertThatCode(() -> …).doesNotThrowAnyException() is handy when testing utility methods that should never fail, especially in refactoring scenarios.
It’s mostly about semantics, your intent is clearer when you pick the right one.
I ran into this myself when migrating tests from JUnit to AssertJ. The key difference is intent:
assertThatThrownBy() is for asserting that an exception is thrown. You pass it a lambda, and then chain assertions like .isInstanceOf(IOException.class).hasMessageContaining("foo").
assertThatCode() is for asserting things about code that may or may not throw. Most often I use it with .doesNotThrowAnyException() to explicitly check that no exception occurs.
For example:
assertThatThrownBy(() -> doSomethingRisky())
.isInstanceOf(IOException.class)
.hasMessageContaining("disk");
assertThatCode(() -> doSomethingSafe())
.doesNotThrowAnyException();
Using the right method makes your tests more readable and communicates intent clearly.