Skip to content
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

Assert return value of callable throwing exception #1652

Open
t-8ch opened this issue Oct 25, 2019 · 7 comments · May be fixed by #2519
Open

Assert return value of callable throwing exception #1652

t-8ch opened this issue Oct 25, 2019 · 7 comments · May be fixed by #2519
Assignees
Labels
type: improvement A general improvement
Milestone

Comments

@t-8ch
Copy link
Contributor

t-8ch commented Oct 25, 2019

Summary

It would be nice if the assertThatCode().doesNotThrowException() API would be able execute functions that return a value and then return that value for further inspection.
This would make it easy to provide a nice description to an otherwise maybe nondescript exception that is thrown.

Example

@Test 
public void intendedApi() {
  int x = assertThatCode(this::doStuff)
    .describedAs("doing things")
    .doesNotThrowAnyException();
  assertThat(x).isEqualTo(3);
}

@Test
public void currentSolution1() {
  int x;
  try {
    x = doStuff();
  } catch (Exception e) {
    fail("doing things");
    return; // never reached; needed because fail() is not recognized by the java compiler as method exit
  }
  assertThat(x).isEqualTo(3);
}

@Test
public void currentSolution2() {
  AtomicReference<Integer> x = new AtomicReference<>();
  assertThatCode(() -> x.set(doStuff()))
    .describedAs("doing things")
    .doesNotThrowAnyException();
  assertThat(x.get()).isEqualTo(3);
}

// utility for demonstration purposes
public int doStuff() throws Exception {
  if (new Random().nextBoolean()) {
    throw new Exception();
  }
  return 3;
}
hosuaby added a commit to hosuaby/assertj-core that referenced this issue Oct 2, 2020
hosuaby added a commit to hosuaby/assertj-core that referenced this issue Oct 2, 2020
hosuaby added a commit to hosuaby/assertj-core that referenced this issue Oct 2, 2020
hosuaby added a commit to hosuaby/assertj-core that referenced this issue Oct 2, 2020
hosuaby added a commit to hosuaby/assertj-core that referenced this issue Oct 2, 2020
@onacit
Copy link
Contributor

onacit commented Oct 29, 2021

How about...

assertThatCode(() -> ...)
        .doesNotThrowAnyExceptionAndReturnsSatisfying(Consumer<? super T> consumer)
;
assertThatCode(() -> ...)
        .doesNotThrowAnyExceptionAndReturnsSatisfying(IntConsumer consumer)
;

?

@joel-costigliola joel-costigliola added this to the 3.22.0 milestone Oct 29, 2021
@joel-costigliola
Copy link
Member

sorry this one fell under our radar, we'll look at it for 3.22.0.
my preference goes for returning object assertion after doesNotThrowAnyException() if technically possible

@scordio scordio modified the milestones: 3.22.0, 3.23.0 Dec 15, 2021
@joel-costigliola joel-costigliola self-assigned this Mar 5, 2022
@joel-costigliola
Copy link
Member

joel-costigliola commented Mar 5, 2022

I don't think it is technically possible in the current codebase to change assertThatCode to support the suggested pattern.
assertThatCode returns an AbstractThrowableAssert and catches any exception from the given the lambda, to support the suggested pattern, assertThatCode would have to return an Object assertion for the values returned by the given lambda code (or null if void) and the caught exception, I feel this is too much of a breaking change.

assertThatCode was added a few years ago, its naming is a bit misleading as you don't always expect a piece of code to throw an exception, in that regard assertThatThrownBy is a better pattern as it's clear we expect the given code to throw an exception thus returning AbstractThrowableAssert makes complete sense.

At the moment IMO the best approach would be to let the test fail with the exception:

@Test
public void otherSolution() {
  int x = doStuff();
  assertThat(x).isEqualTo(3);
}

I know the test won't fail with an assertion error but with the exception from doStuff, I think that's an acceptable compromise and that's the option with the simplest test code.

I'm still thinking we could something like though:

// chains Object assertion
assertThatCodeReturnedValue(() -> doStuff()).isEqualTo(3);

// use asInstanceOf to chain specific typed assertion:
assertThatCodeReturnedValue(() -> doStuff()).asInstanceOf(INTEGER).isLessThan(10);

WDYT?

@joel-costigliola
Copy link
Member

Better naming:

// chains Object assertion
assertThatValueReturnedBy(() -> doStuff()).isEqualTo(3);

// use asInstanceOf to chain specific typed assertion:
assertThatValueReturnedBy(() -> doStuff()).asInstanceOf(INTEGER).isLessThan(10);

joel-costigliola added a commit that referenced this issue Mar 6, 2022
@scordio
Copy link
Member

scordio commented Mar 7, 2022

I think it would be possible to add an overloaded version of assertThatCode() accepting Callable and exposing a result() method, so we would be able to write something like:

assertThatCode(() -> "asd".isEmpty()) // executes Callable::call storing either the result or the thrown exception
  .doesNotThrowAnyException()
  .result() // returns AbstractObjectAssert
  .isEqualTo(42);

still keeping compatibility with everything else which is not a Callable:

assertThatCode(() -> System.out.println("")) // AbstractThrowableAssert
  .doesNotThrowAnyException();

I'll prepare a draft PR for evaluation.

@scordio scordio linked a pull request Mar 8, 2022 that will close this issue
@scordio scordio added the type: improvement A general improvement label May 6, 2022
@scordio scordio modified the milestones: 3.23.0, 3.24.0 May 6, 2022
@scordio scordio removed this from the 3.24.0 milestone Jul 22, 2022
@onacit
Copy link
Contributor

onacit commented May 3, 2023

Hi @scordio. Is this in progress? Thanks.

@scordio
Copy link
Member

scordio commented May 3, 2023

Hi @onacit, yes, see #2519.

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