test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
static final String EXPECTED_RESULT = "expected result";
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
public static class Mutable {

    private int forDemonstration;

    void setForDemonstration(int forDemonstration) {
        this.forDemonstration = forDemonstration;
    }
}
test-as-you-think-core/src/test/java/testasyouthink/fixture/ParameterizedSystemUnderTest.java
public class ParameterizedSystemUnderTest<$Parameter1, $Parameter2, $Parameter3> {

    public void voidMethodWithParameter($Parameter1 parameter) {}

    public String nonVoidMethodWithParameter($Parameter1 parameter) {
        return null;
    }

    public void voidMethodWithTwoParameters($Parameter1 parameter1, $Parameter2 parameter2) {}

    public String nonVoidMethodWithTwoParameters($Parameter1 parameter1, $Parameter2 parameter2) {
        return null;
    }

    public void voidMethodWithThreeParameters($Parameter1 parameter1, $Parameter2 parameter2, $Parameter3 parameter3) {}

    public String nonVoidMethodWithThreeParameters($Parameter1 parameter1, $Parameter2 parameter2,
            $Parameter3 parameter3) {
        return null;
    }

    public void failWithParameter($Parameter1 parameter) throws Throwable {}

    public void failWithTwoParameters($Parameter1 parameter1, $Parameter2 parameter2) throws Throwable {}

    public void failWithThreeParameters($Parameter1 parameter1, $Parameter2 parameter2,
            $Parameter3 parameter3) throws Throwable {}

    public String nonVoidFailWithParameter($Parameter1 parameter) throws Throwable {
        return null;
    }

    public String nonVoidFailWithTwoParameters($Parameter1 parameter1, $Parameter2 parameter2) throws Throwable {
        return null;
    }

    public String nonVoidFailWithThreeParameters($Parameter1 parameter1, $Parameter2 parameter2,
            $Parameter3 parameter3) throws Throwable {
        return null;
    }
}
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
static ArgumentPreparationAssertions assertThatFailure(Throwable thrown) {
    return new ArgumentPreparationAssertions(thrown);
}
test-as-you-think-core/src/main/java/testasyouthink/TestAsYouThink.java
public static <$SystemUnderTest> Given<$SystemUnderTest> givenSut($SystemUnderTest systemUnderTest) {
    return new GivenWhenSteps<>(systemUnderTest);
}
test-as-you-think-core/src/test/java/testasyouthink/fixture/GivenWhenThenDefinition.java
public class GivenWhenThenDefinition {

    private static final int DEFAULT_NUMBER_OF_STEPS = 1;

    public static GivenWhenThenDefinition orderedSteps() {
        return orderedSteps(DEFAULT_NUMBER_OF_STEPS, DEFAULT_NUMBER_OF_STEPS, DEFAULT_NUMBER_OF_STEPS);
    }

    public static GivenWhenThenDefinition orderedSteps(int numberOfGivenSteps) {
        return orderedSteps(numberOfGivenSteps, DEFAULT_NUMBER_OF_STEPS, DEFAULT_NUMBER_OF_STEPS);
    }

    public static GivenWhenThenDefinition orderedSteps(int numberOfGivenSteps, int numberOfThenSteps) {
        return orderedSteps(numberOfGivenSteps, DEFAULT_NUMBER_OF_STEPS, numberOfThenSteps);
    }

    public static GivenWhenThenDefinition orderedSteps(int numberOfGivenSteps, int numberOfWhenSteps,
            int numberOfThenSteps) {
        GivenWhenThenDefinition mock = strictMock(GivenWhenThenDefinition.class);

        if (numberOfGivenSteps > 0) {
            mock.givenAContextThatDefinesTheInitialStateOfTheSystem();
            expectLastCall().times(numberOfGivenSteps);
        }
        if (numberOfWhenSteps > 0) {
            mock.whenAnEventHappensInRelationToAnActionOfTheConsumer();
            expectLastCall().times(numberOfWhenSteps);
        }
        if (numberOfThenSteps > 0) {
            mock.thenTheActualResultIsInKeepingWithTheExpectedResult();
            expectLastCall().times(numberOfThenSteps);
        }

        replay(mock);
        return mock;
    }

    public void givenAContextThatDefinesTheInitialStateOfTheSystem() {}

    public void whenAnEventHappensInRelationToAnActionOfTheConsumer() {}

    public void thenTheActualResultIsInKeepingWithTheExpectedResult() {}
}
test-as-you-think-core/src/main/java/testasyouthink/TestAsYouThink.java
public static <$SystemUnderTest> Given<$SystemUnderTest> givenSutClass(Class<$SystemUnderTest> sutClass) {
    return new GivenWhenSteps<>(sutClass);
}
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
static final String GIVEN_STRING = "given argument";
test-as-you-think-core/src/main/java/testasyouthink/function/CheckedSupplier.java
public interface CheckedSupplier<R> {

    R get() throws Throwable;
}
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
static final int GIVEN_INTEGER = 201705;
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
static final boolean GIVEN_BOOLEAN = false;
test-as-you-think-core/src/test/java/testasyouthink/fixture/Specifications.java
public static final String EXPECTED_EXECUTION_FAILURE_MESSAGE = "Fails to execute the target method " //
        + "of the system under test because of an unexpected failure!";
test-as-you-think-core/src/test/java/testasyouthink/fixture/SystemUnderTest.java
public class SystemUnderTest {

    private GivenWhenThenDefinition givenWhenThenDefinition;
    private String state;

    public SystemUnderTest() {}

    public SystemUnderTest(GivenWhenThenDefinition givenWhenThenDefinition) {
        this.givenWhenThenDefinition = givenWhenThenDefinition;
    }

    private void whenAnEventHappens() {
        if (givenWhenThenDefinition != null) {
            givenWhenThenDefinition.whenAnEventHappensInRelationToAnActionOfTheConsumer();
        }
    }

    public String nonVoidMethod() {
        whenAnEventHappens();
        changeState();
        return "expected result";
    }

    public void voidMethod() {
        whenAnEventHappens();
        changeState();
    }

    public void voidMethodWithParameter(String parameter) {
        voidMethod();
    }

    public String nonVoidMethodWithParameter(String parameter) {
        return nonVoidMethod();
    }

    public void voidMethodWithTwoParameters(String parameter1, Integer parameter2) {
        voidMethod();
    }

    public String nonVoidMethodWithTwoParameters(String parameter1, Integer parameter2) {
        return nonVoidMethod();
    }

    public void voidMethodWithThreeParameters(String parameter1, Integer parameter2, Boolean parameter3) {
        voidMethod();
    }

    public String nonVoidMethodWithThreeParameters(String parameter1, Integer parameter2, Boolean parameter3) {
        return nonVoidMethod();
    }

    public void failWithParameter(String parameter) throws Throwable {}

    public void failWithTwoParameters(String parameter1, Integer parameter2) throws Throwable {}

    public void failWithThreeParameters(String parameter1, Integer parameter2, Boolean parameter3) throws Throwable {}

    public String nonVoidFailWithParameter(String parameter) throws Throwable {
        return null;
    }

    public String nonVoidFailWithTwoParameters(String parameter1, Integer parameter2) throws Throwable {
        return null;
    }

    public String nonVoidFailWithThreeParameters(String parameter1, Integer parameter2,
            Boolean parameter3) throws Throwable {
        return null;
    }

    public void voidMethodWithThrowsClause() throws Throwable {}

    public String nonVoidMethodWithThrowsClause() throws Throwable {
        return null;
    }

    public String methodWithThrowsClause() throws Throwable {
        return null;
    }

    private void changeState() {
        state = "state updated";
    }

    public void setGivenWhenThenDefinition(GivenWhenThenDefinition givenWhenThenDefinition) {
        this.givenWhenThenDefinition = givenWhenThenDefinition;
    }

    public String getState() {
        return state;
    }
}
test-as-you-think-core/src/main/java/testasyouthink/preparation/PreparationError.java
public class PreparationError extends Error {

    public PreparationError(String message, Throwable cause) {
        super(message, cause);
    }
}
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
public static class MutableButUninstantiable {

    public MutableButUninstantiable() throws InstantiationException {
        throw new InstantiationException("Impossible to instantiate it!");
    }
}
test-as-you-think-core/src/test/java/testasyouthink/fixture/UnexpectedException.java
public class UnexpectedException extends Exception {

    public UnexpectedException() {
        super();
    }

    public UnexpectedException(String message) {
        super(message);
    }
}
test-as-you-think-core/src/test/java/testasyouthink/GivenArgumentsTest.java
static final String EXPECTED_ERROR_MESSAGE = "Expected error message";
test-as-you-think-core/src/main/java/testasyouthink/execution/ExecutionError.java
public class ExecutionError extends Error {

    public ExecutionError(String message, Throwable cause) {
        super(message, cause);
    }
}
test-as-you-think-core/src/test/java/testasyouthink/fixture/ExpectedException.java
public class ExpectedException extends Exception {

    public ExpectedException() {
        super();
    }

    public ExpectedException(String message) {
        super(message);
    }

    public ExpectedException(String message, Throwable cause) {
        super(message, cause);
    }
}


Question: import org.assertj.core.api.AbstractAssert;
import org.easymock.IMocksControl;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import testasyouthink.GivenArgumentsTest.Parameter.Mutable;
import testasyouthink.GivenArgumentsTest.Parameter.MutableButUninstantiable;
import testasyouthink.execution.ExecutionError;
import testasyouthink.fixture.ExpectedException;
import testasyouthink.fixture.GivenWhenThenDefinition;
import testasyouthink.fixture.ParameterizedSystemUnderTest;
import testasyouthink.fixture.SystemUnderTest;
import testasyouthink.fixture.UnexpectedException;
import testasyouthink.function.CheckedSupplier;
import testasyouthink.preparation.PreparationError;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createStrictControl;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.mockito.Mockito.mock;
import static testasyouthink.ArgumentPreparationAssertions.assertThatFailure;
import static testasyouthink.GivenArgumentsTest.GivenData.EXPECTED_ERROR_MESSAGE;
import static testasyouthink.GivenArgumentsTest.GivenData.EXPECTED_RESULT;
import static testasyouthink.GivenArgumentsTest.GivenData.GIVEN_BOOLEAN;
import static testasyouthink.GivenArgumentsTest.GivenData.GIVEN_INTEGER;
import static testasyouthink.GivenArgumentsTest.GivenData.GIVEN_STRING;
import static testasyouthink.TestAsYouThink.givenSut;
import static testasyouthink.TestAsYouThink.givenSutClass;
import static testasyouthink.fixture.Specifications.ExpectedMessage.EXPECTED_EXECUTION_FAILURE_MESSAGE;
                            .then(() -> givenWhenThenDefinitionMock
                                    .thenTheActualResultIsInKeepingWithTheExpectedResult()));

                    // THEN
                    assertThatFailure(thrown).happensWhilePreparingArgument();
                }

                @Test
                void should_fail_to_instantiate_one_argument_with_its_mutable_type() {
                    //GIVEN
                    class SystemUnderTestWithUninstantiableParameter extends
                            ParameterizedSystemUnderTest<MutableButUninstantiable, Void, Void> {}
                    SystemUnderTestWithUninstantiableParameter sutWithUninstantiableParameter = mock(
                            SystemUnderTestWithUninstantiableParameter.class);

                    // WHEN
                    Throwable thrown = catchThrowable(() -> givenSut(sutWithUninstantiableParameter)
                            .givenArgument(MutableButUninstantiable.class, mutableButUninstantiable -> {})
                            .whenSutRuns(ParameterizedSystemUnderTest::voidMethodWithParameter)
                            .then(() -> givenWhenThenDefinitionMock
                                    .thenTheActualResultIsInKeepingWithTheExpectedResult()));

                    // THEN
                    LOGGER.debug("Stack trace", thrown);
                    assertThat(thrown)
                            .isInstanceOf(PreparationError.class)
                            .hasMessage("Fails to instantiate the argument of the " //
                                    + "testasyouthink.GivenArgumentsTest$Parameter$MutableButUninstantiable type!")
                            .hasCauseInstanceOf(InstantiationException.class);
                }

                @Test
                void should_fail_to_prepare_one_argument_with_its_mutable_type() {
                    //GIVEN
                    class SystemUnderTestWithMutableParameter extends ParameterizedSystemUnderTest<Parameter.Mutable,
                            Void, Void> {}
                    SystemUnderTestWithMutableParameter sutWithMutableParameter = mock(
                            SystemUnderTestWithMutableParameter.class);

                    // WHEN
                    Throwable thrown = catchThrowable(() -> givenSut(sutWithMutableParameter)
                            .givenArgument(Parameter.Mutable.class, mutable -> {
                                throw new UnexpectedException();
                            })
                            .whenSutRuns(ParameterizedSystemUnderTest::voidMethodWithParameter)
                            .then(() -> givenWhenThenDefinitionMock
                                    .thenTheActualResultIsInKeepingWithTheExpectedResult()));

                    // THEN
                    assertThatFailure(thrown).happensWhilePreparingMutableArgument();
                }
            }

            @Nested
            class When_a_failure_happens {

                @Nested
                class When_an_unexpected_failure_happens {

                    @Test
                    void should_fail_to_execute_a_void_target_method_with_one_parameter() throws Throwable {
                        // GIVEN
                        givenWhenThenDefinitionMock.givenAContextThatDefinesTheInitialStateOfTheSystem();
                        systemUnderTestMock.failWithParameter(GIVEN_STRING);
                        expectLastCall().andThrow(new UnexpectedException());
                        mocksControl.replay();

                        // WHEN
                        Throwable thrown = catchThrowable(() -> givenSut(systemUnderTestMock)
                                .givenArgument(() -> {
                                    givenWhenThenDefinitionMock.givenAContextThatDefinesTheInitialStateOfTheSystem();
                                    return GIVEN_STRING;
                                })
                                .when(SystemUnderTest::failWithParameter)
                                .then(() -> givenWhenThenDefinitionMock
                                        .thenTheActualResultIsInKeepingWithTheExpectedResult()));

                        // THEN
                        LOGGER.debug("Stack trace", thrown);
                        assertThat(thrown)
                                .isInstanceOf(ExecutionError.class)
                                .hasMessage(EXPECTED_EXECUTION_FAILURE_MESSAGE)
                                .hasCauseInstanceOf(UnexpectedException.class);
                    }

                    @Test
                    void should_fail_to_execute_a_non_void_target_method_with_one_parameter() throws Throwable {
                        // GIVEN
                        givenWhenThenDefinitionMock.givenAContextThatDefinesTheInitialStateOfTheSystem();
                        expect(systemUnderTestMock.nonVoidFailWithParameter(GIVEN_STRING)).andThrow(
                                new UnexpectedException());
                        mocksControl.replay();

                        // WHEN
                        Throwable thrown = catchThrowable(() -> givenSut(systemUnderTestMock)
                                .givenArgument(() -> {
                                    givenWhenThenDefinitionMock.givenAContextThatDefinesTheInitialStateOfTheSystem();
                                    return GIVEN_STRING;
                                })
                                .when(SystemUnderTest::nonVoidFailWithParameter)
                                .then(() -> givenWhenThenDefinitionMock
                                        .thenTheActualResultIsInKeepingWithTheExpectedResult()));

                        // THEN
                        LOGGER.debug("Stack trace", thrown);
                        assertThat(thrown)
                                .isInstanceOf(ExecutionError.class)
                                .hasMessage(EXPECTED_EXECUTION_FAILURE_MESSAGE)
                                .hasCauseInstanceOf(UnexpectedException.class);
                    }
                }

                @Nested
                class When_an_expected_failure_happens {

                    @Test
                    void should_verify_the_sut_fails_given_one_method_parameter() throws Throwable {
                        // GIVEN
                        givenWhenThenDefinitionMock.givenAContextThatDefinesTheInitialStateOfTheSystem();
                        systemUnderTestMock.failWithParameter(GIVEN_STRING);
Answer:                         expectLastCall().andThrow(new ExpectedException(EXPECTED_ERROR_MESSAGE));