diff --git a/.github/workflows/binary-compatibility.yml b/.github/workflows/binary-compatibility.yml index b8375dd02f..2801062e80 100644 --- a/.github/workflows/binary-compatibility.yml +++ b/.github/workflows/binary-compatibility.yml @@ -26,6 +26,9 @@ jobs: distribution: 'zulu' java-version: '25' cache: 'maven' + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Checkout `${{ github.base_ref }}` into subfolder uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 @@ -56,6 +59,9 @@ jobs: distribution: 'zulu' java-version: '25' cache: 'maven' + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Compare with the latest release run: > diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6b1be962fd..85b8a8cab1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,7 +31,7 @@ jobs: - name: Checkout repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 + uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -44,6 +44,6 @@ jobs: java-version: '25' cache: 'maven' - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 + uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 50f2edf5aa..59d74685a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,7 +5,7 @@ on: [push, pull_request] permissions: {} env: - MAVEN_ARGS: -B -V -ntp -e -Djansi.passthrough=true -Dstyle.color=always + MAVEN_ARGS: -B -V -ntp -e -Djansi.passthrough=true -Dstyle.color=always --debug jobs: @@ -17,6 +17,8 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] runs-on: ${{ matrix.os }} + permissions: + checks: write steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 @@ -26,8 +28,16 @@ jobs: distribution: 'zulu' java-version: '25' cache: 'maven' + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Test run: ./mvnw $MAVEN_ARGS verify + - name: Publish Test Report + uses: mikepenz/action-junit-report@a294a61c909bd8a4b563024a2faa28897fd53ebc + if: success() || failure() # always run even if the previous step fails + with: + report_paths: '**/target/surefire-reports/TEST-*.xml' java-oracle: @@ -47,6 +57,9 @@ jobs: website: jdk.java.net release: ${{ matrix.java }} version: latest + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Test run: ./mvnw $MAVEN_ARGS verify @@ -68,6 +81,9 @@ jobs: distribution: 'zulu' java-version: '25' cache: 'maven' + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Test run: ./mvnw $MAVEN_ARGS -Dgroovy.version=${{ matrix.groovy }} verify @@ -89,6 +105,9 @@ jobs: distribution: 'zulu' java-version: '25' cache: 'maven' + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Test run: ./mvnw $MAVEN_ARGS -Dkotlin.version=${{ matrix.kotlin }} verify @@ -124,6 +143,9 @@ jobs: distribution: 'zulu' java-version: '25' cache: 'maven' + - uses: testlens-app/setup-testlens@maven-support + with: + instrumentation-version: '1.5.1-SNAPSHOT' - name: Test with Sonar run: > ./mvnw $MAVEN_ARGS verify sonar:sonar diff --git a/.github/workflows/qodana.yml b/.github/workflows/qodana.yml index 9ec4344dba..4feb71989e 100644 --- a/.github/workflows/qodana.yml +++ b/.github/workflows/qodana.yml @@ -16,7 +16,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit fetch-depth: 0 # a full history is required for pull request analysis - name: 'Qodana Scan' - uses: JetBrains/qodana-action@99ec27a55aaaf5ba2fd7e5816e66231caed7a72a #v2025.2.4 + uses: JetBrains/qodana-action@42dad391966aca8ca344ca2340a7f43a5507e9b2 #v2025.3.1 with: # https://youtrack.jetbrains.com/issue/QD-6342 – no option to disable emojis post-pr-comment: false diff --git a/.gitignore b/.gitignore index ffb55899cf..b5304d9c56 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ target #IntelliJ project files .idea/* +!/.idea/inspectionProfiles +!/.idea/inspectionProfiles/Project_Default.xml !/.idea/icon.svg *.iml out diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000..3084134295 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index c0bcafe984..8dea6c227c 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,3 +1,3 @@ wrapperVersion=3.3.4 distributionType=only-script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip diff --git a/assertj-core/pom.xml b/assertj-core/pom.xml index b8fb16b0ab..5bb8fbe298 100644 --- a/assertj-core/pom.xml +++ b/assertj-core/pom.xml @@ -27,7 +27,7 @@ ${project.basedir}/../ false - 1.18.2 + 1.18.3 @@ -107,7 +107,7 @@ nl.jqno.equalsverifier equalsverifier - 4.2.5 + 4.3 test @@ -251,7 +251,6 @@ -exportcontents: \ !org.assertj.core.internal.*,\ org.assertj.core.* - -noclassforname: true -noextraheaders: true -snapshot: SNAPSHOT -removeheaders: Private-Package diff --git a/assertj-core/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java index f1a70538bf..b4788f2aa8 100644 --- a/assertj-core/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java +++ b/assertj-core/src/main/java/org/assertj/core/api/AbstractSoftAssertions.java @@ -25,6 +25,7 @@ public abstract class AbstractSoftAssertions extends DefaultAssertionErrorCollector implements SoftAssertionsProvider, InstanceOfAssertFactories { + private static final AssertionErrorCreator ASSERTION_ERROR_CREATOR = new AssertionErrorCreator(); final SoftProxies proxies; protected AbstractSoftAssertions() { @@ -32,11 +33,9 @@ protected AbstractSoftAssertions() { proxies = new SoftProxies(this); } - private static final AssertionErrorCreator ASSERTION_ERROR_CREATOR = new AssertionErrorCreator(); - public static void assertAll(AssertionErrorCollector collector) { List errors = collector.assertionErrorsCollected(); - if (!errors.isEmpty()) throw ASSERTION_ERROR_CREATOR.multipleSoftAssertionsError(errors); + if (!errors.isEmpty()) throw ASSERTION_ERROR_CREATOR.multipleAssertionsError(errors); } @Override @@ -53,7 +52,7 @@ public void assertAll() { /** * Fails with the given message. * - * @param dummy return value type + * @param dummy return value type * @param failureMessage error message. * @return nothing, it's just to be used in {@code doSomething(optional.orElseGet(() -> softly.fail("boom")));}. * @since 2.6.0 / 3.6.0 @@ -84,9 +83,9 @@ public T fail() { /** * Fails with the given message built like {@link String#format(String, Object...)}. * - * @param dummy return value type + * @param dummy return value type * @param failureMessage error message. - * @param args Arguments referenced by the format specifiers in the format string. + * @param args Arguments referenced by the format specifiers in the format string. * @return nothing, it's just to be used in {@code doSomething(optional.orElseGet(() -> softly.fail("boom")));}. * @since 2.6.0 / 3.6.0 */ @@ -99,9 +98,9 @@ public T fail(String failureMessage, Object... args) { /** * Fails with the given message and with the {@link Throwable} that caused the failure. * - * @param dummy return value type + * @param dummy return value type * @param failureMessage error message. - * @param realCause cause of the error. + * @param realCause cause of the error. * @return nothing, it's just to be used in {@code doSomething(optional.orElseGet(() -> softly.fail("boom")));}. * @since 2.6.0 / 3.6.0 */ @@ -120,7 +119,7 @@ public T fail(String failureMessage, Throwable realCause) { * Example: *
 doSomething(optional.orElseGet(() -> softly.fail(cause)));
* - * @param dummy return value type + * @param dummy return value type * @param realCause cause of the error. * @return nothing, it's just to be used in {@code doSomething(optional.orElseGet(() -> softly.fail(cause)));}. * @since 3.26.0 @@ -139,10 +138,9 @@ public T fail(Throwable realCause) { * * @param throwableClass the Throwable class that was expected to be thrown. * @throws AssertionError with a message explaining that a {@link Throwable} of given class was expected to be thrown but had - * not been. - * @since 2.6.0 / 3.6.0 - * + * not been. * @see #shouldHaveThrown(Class) + * @since 2.6.0 / 3.6.0 */ @Contract("_ -> fail") public void failBecauseExceptionWasNotThrown(Class throwableClass) { @@ -155,7 +153,7 @@ public void failBecauseExceptionWasNotThrown(Class throwabl * * @param throwableClass the Throwable class that was expected to be thrown. * @throws AssertionError with a message explaining that a {@link Throwable} of given class was expected to be thrown but had - * not been. + * not been. * @since 2.6.0 / 3.6.0 */ @Contract("_ -> fail") @@ -166,6 +164,7 @@ public void shouldHaveThrown(Class throwableClass) { /** * Returns a copy of list of soft assertions collected errors. + * * @return a copy of list of soft assertions collected errors. */ public List errorsCollected() { diff --git a/assertj-core/src/main/java/org/assertj/core/api/AutoCloseableBDDSoftAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/AutoCloseableBDDSoftAssertions.java index b964b442ac..7043fab78e 100644 --- a/assertj-core/src/main/java/org/assertj/core/api/AutoCloseableBDDSoftAssertions.java +++ b/assertj-core/src/main/java/org/assertj/core/api/AutoCloseableBDDSoftAssertions.java @@ -94,19 +94,19 @@ *
  * org.assertj.core.error.AssertJMultipleFailuresError:
  * Multiple Failures (4 failure)
- * -- failure 1 --
+ * -- error 1 --
  * [Living Guests]
  * expected: 7
  *  but was: 6
- * -- failure 2 --
+ * -- error 2 --
  * [Library]
  * expected: clean
  *  but was: messy
- * -- failure 3 --
+ * -- error 3 --
  * [Candlestick]
  * expected: pristine
  *  but was: bent
- * -- failure 4 --
+ * -- error 4 --
  * [Professor]
  * expected: well kempt
  *  but was: bloodied and disheveled
diff --git a/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java b/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java
index 9ca637de8c..393c1b2f47 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/BDDAssumptions.java
@@ -1308,7 +1308,7 @@ public static  ObjectAssert given(T actual) {
    *}
* * @param the type of elements of the actual objects' array. - * @param actual the actual objects' array to be validated.. + * @param actual the actual objects' array to be validated. * @return the {@link AbstractObjectArrayAssert} assertion object to be used for assumptions. * @since 3.14.0 */ @@ -1338,7 +1338,7 @@ public static ObjectArrayAssert given(T[] actual) { *} * * @param the type of elements of the actual objects' two-dimensional array. - * @param actual the actual objects' two-dimensional array to be validated.. + * @param actual the actual objects' two-dimensional array to be validated. * @return the {@link Object2DArrayAssert} assertion object to be used for assumptions. * @since 3.17.0 */ diff --git a/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertions.java index 7239f2f375..28287f1d2e 100644 --- a/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertions.java +++ b/assertj-core/src/main/java/org/assertj/core/api/BDDSoftAssertions.java @@ -79,19 +79,19 @@ *
  * org.assertj.core.error.AssertJMultipleFailuresError:
  * Multiple Failures (4 failure)
- * -- failure 1 --
+ * -- error 1 --
  * [Living Guests]
  * expected: 7
  *  but was: 6
- * -- failure 2 --
+ * -- error 2 --
  * [Library]
  * expected: clean
  *  but was: messy
- * -- failure 3 --
+ * -- error 3 --
  * [Candlestick]
  * expected: pristine
  *  but was: bent
- * -- failure 4 --
+ * -- error 4 --
  * [Professor]
  * expected: well kempt
  *  but was: bloodied and disheveled
diff --git a/assertj-core/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java b/assertj-core/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java
index e3656a31b3..86a3b294b4 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/DefaultAssertionErrorCollector.java
@@ -22,8 +22,6 @@
 import java.util.List;
 import java.util.Optional;
 
-import org.assertj.core.util.Throwables;
-
 public class DefaultAssertionErrorCollector implements AssertionErrorCollector {
 
   // Marking this field as volatile doesn't ensure complete thread safety
@@ -158,7 +156,7 @@ public boolean wasSuccess() {
    * @return decorated list
   */
   protected  List decorateErrorsCollected(List errors) {
-    return Throwables.addLineNumberToErrorMessages(errors);
+    return (List) errors;
   }
 
 }
diff --git a/assertj-core/src/main/java/org/assertj/core/api/SoftAssertions.java b/assertj-core/src/main/java/org/assertj/core/api/SoftAssertions.java
index ed2e346ba7..e1bb8e95f9 100644
--- a/assertj-core/src/main/java/org/assertj/core/api/SoftAssertions.java
+++ b/assertj-core/src/main/java/org/assertj/core/api/SoftAssertions.java
@@ -79,19 +79,19 @@
  * 
  * org.assertj.core.error.AssertJMultipleFailuresError:
  * Multiple Failures (4 failure)
- * -- failure 1 --
+ * -- error 1 --
  * [Living Guests]
  * expected: 7
  *  but was: 6
- * -- failure 2 --
+ * -- error 2 --
  * [Library]
  * expected: clean
  *  but was: messy
- * -- failure 3 --
+ * -- error 3 --
  * [Candlestick]
  * expected: pristine
  *  but was: bent
- * -- failure 4 --
+ * -- error 4 --
  * [Professor]
  * expected: well kempt
  *  but was: bloodied and disheveled
diff --git a/assertj-core/src/main/java/org/assertj/core/error/AssertJMultipleFailuresError.java b/assertj-core/src/main/java/org/assertj/core/error/AssertJMultipleFailuresError.java
deleted file mode 100644
index 6044ed5383..0000000000
--- a/assertj-core/src/main/java/org/assertj/core/error/AssertJMultipleFailuresError.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2012-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.assertj.core.error;
-
-import static org.assertj.core.util.Throwables.addLineNumberToErrorMessages;
-
-import java.io.Serial;
-import java.util.List;
-
-import org.opentest4j.MultipleFailuresError;
-
-/**
- * AssertJ version of {@link MultipleFailuresError} to get more control on the error formatting.
- */
-public class AssertJMultipleFailuresError extends MultipleFailuresError {
-
-  @Serial
-  private static final long serialVersionUID = 1L;
-
-  private static final String EOL = System.lineSeparator();
-  private static final String ERROR_SEPARATOR = EOL + "-- failure %d --";
-
-  private String heading;
-  private Object objectUnderTest;
-  private boolean showObjectUnderTest;
-
-  public AssertJMultipleFailuresError(String heading, List failures) {
-    super(heading, failures);
-    this.heading = heading;
-  }
-
-  public AssertJMultipleFailuresError(String heading, Object objectUnderTest, List failures) {
-    super(heading, failures);
-    this.heading = heading;
-    this.objectUnderTest = objectUnderTest;
-    this.showObjectUnderTest = true;
-  }
-
-  @Override
-  public String getMessage() {
-
-    List failures = getFailures();
-    int failureCount = failures.size();
-
-    if (failureCount == 0) return super.getMessage();
-
-    heading = isBlank(heading) ? "Multiple Failures" : heading.trim();
-    String beginningOfErrorMessage = showObjectUnderTest ? "%nFor %s,%n".formatted(objectUnderTest) : EOL;
-    var builder = new StringBuilder(beginningOfErrorMessage).append(heading)
-                                                            .append(" (")
-                                                            .append(failureCount).append(" ")
-                                                            .append(failureCount == 1 ? "failure" : "failures")
-                                                            .append(")");
-    List failuresWithLineNumbers = addLineNumberToErrorMessages(failures);
-    for (int i = 0; i < failureCount; i++) {
-      builder.append(errorSeparator(i + 1));
-      String message = nullSafeMessage(failuresWithLineNumbers.get(i));
-      // when we have a description, we add a line before for readability
-      if (hasDescription(message)) builder.append(EOL);
-      builder.append(message);
-    }
-
-    return builder.toString();
-  }
-
-  private String errorSeparator(int errorNumber) {
-    return ERROR_SEPARATOR.formatted(errorNumber);
-  }
-
-  private boolean hasDescription(String message) {
-    return message.startsWith("[");
-  }
-
-  private static boolean isBlank(String str) {
-    return str == null || str.trim().isEmpty();
-  }
-
-  private static String nullSafeMessage(Throwable failure) {
-    return isBlank(failure.getMessage()) ? " in " + failure.getClass().getName() : failure.getMessage();
-  }
-
-}
diff --git a/assertj-core/src/main/java/org/assertj/core/error/AssertionErrorCreator.java b/assertj-core/src/main/java/org/assertj/core/error/AssertionErrorCreator.java
index 574f5d04ae..e9c96db33e 100644
--- a/assertj-core/src/main/java/org/assertj/core/error/AssertionErrorCreator.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/AssertionErrorCreator.java
@@ -16,27 +16,18 @@
 package org.assertj.core.error;
 
 import static org.assertj.core.util.Arrays.array;
-import static org.assertj.core.util.Throwables.describeErrors;
 
 import java.util.List;
 import java.util.Optional;
 
-import org.assertj.core.api.SoftAssertionError;
 import org.assertj.core.description.Description;
 import org.assertj.core.internal.Failures;
 import org.assertj.core.presentation.Representation;
-import org.assertj.core.util.introspection.PropertyOrFieldSupport;
 
 public class AssertionErrorCreator {
 
   private static final Class[] MSG_ARG_TYPES_FOR_ASSERTION_FAILED_ERROR = array(String.class, Object.class, Object.class);
 
-  private static final Class[] MULTIPLE_FAILURES_ERROR_ARGUMENT_TYPES = array(String.class, List.class);
-
-  private static final Class[] MULTIPLE_FAILURES_ERROR_ARGUMENT_TYPES_WITH_ACTUAL_ROOT_INSTANCE = array(String.class,
-                                                                                                           Object.class,
-                                                                                                           List.class);
-
   // TODO reduce the visibility of the fields annotated with @VisibleForTesting
   ConstructorInvoker constructorInvoker;
 
@@ -76,67 +67,13 @@ public AssertionError assertionError(String message) {
 
   // multiple assertions error
 
-  public AssertionError multipleSoftAssertionsError(List errors) {
-    Optional multipleFailuresError = tryBuildingMultipleFailuresError(errors);
-    return multipleFailuresError.orElse(new SoftAssertionError(describeErrors(errors)));
+  public AssertionError multipleAssertionsError(List errors) {
+    return multipleAssertionsError(null, null, errors);
   }
 
-  public AssertionError multipleAssertionsError(Description description, Object objectUnderTest,
-                                                List errors) {
-    String heading = headingFrom(description);
-    Optional multipleFailuresError = tryBuildingMultipleFailuresError(heading, objectUnderTest, errors);
-    return multipleFailuresError.orElse(new MultipleAssertionsError(description, objectUnderTest, errors));
+  public AssertionError multipleAssertionsError(Description description, Object objectUnderTest, List errors) {
+    MultipleAssertionsError multipleAssertionsError = new MultipleAssertionsError(description, objectUnderTest, errors);
+    Failures.instance().removeAssertJRelatedElementsFromStackTraceIfNeeded(multipleAssertionsError);
+    return multipleAssertionsError;
   }
-
-  public void tryThrowingMultipleFailuresError(List errorsCollected) {
-    tryBuildingMultipleFailuresError(errorsCollected).ifPresent(AssertionErrorCreator::throwError);
-  }
-
-  // syntactic sugar
-  private static void throwError(AssertionError error) {
-    throw error;
-  }
-
-  private static String headingFrom(Description description) {
-    return description == null ? null : DescriptionFormatter.instance().format(description);
-  }
-
-  private Optional tryBuildingMultipleFailuresError(List errorsCollected) {
-    return tryBuildingMultipleFailuresError(null, null, errorsCollected);
-  }
-
-  private Optional tryBuildingMultipleFailuresError(String heading,
-                                                                    Object objectUnderTest,
-                                                                    List errorsCollected) {
-    if (errorsCollected.isEmpty()) return Optional.empty();
-    try {
-      Object[] constructorArguments = array(heading, errorsCollected);
-      Object multipleFailuresError = constructorInvoker.newInstance("org.opentest4j.MultipleFailuresError",
-                                                                    MULTIPLE_FAILURES_ERROR_ARGUMENT_TYPES,
-                                                                    constructorArguments);
-      if (multipleFailuresError instanceof AssertionError) { // means that we were able to build a MultipleFailuresError
-        List failures = extractFailuresOf(multipleFailuresError);
-        // we switch to AssertJMultipleFailuresError in order to control the formatting of the error message.
-        // we use reflection to avoid making opentest4j a required dependency
-        AssertionError assertionError = objectUnderTest != null
-            ? (AssertionError) constructorInvoker.newInstance("org.assertj.core.error.AssertJMultipleFailuresError",
-                                                              MULTIPLE_FAILURES_ERROR_ARGUMENT_TYPES_WITH_ACTUAL_ROOT_INSTANCE,
-                                                              array(heading, objectUnderTest, failures))
-            : (AssertionError) constructorInvoker.newInstance("org.assertj.core.error.AssertJMultipleFailuresError",
-                                                              MULTIPLE_FAILURES_ERROR_ARGUMENT_TYPES,
-                                                              array(heading, failures));
-        Failures.instance().removeAssertJRelatedElementsFromStackTraceIfNeeded(assertionError);
-        return Optional.of(assertionError);
-      }
-    } catch (@SuppressWarnings("unused") Exception e) {
-      // do nothing, MultipleFailuresError was not in the classpath
-    }
-    return Optional.empty();
-  }
-
-  @SuppressWarnings("unchecked")
-  private static List extractFailuresOf(Object multipleFailuresError) {
-    return (List) PropertyOrFieldSupport.EXTRACTION.getValueOf("failures", multipleFailuresError);
-  }
-
 }
diff --git a/assertj-core/src/main/java/org/assertj/core/error/MultipleAssertionsError.java b/assertj-core/src/main/java/org/assertj/core/error/MultipleAssertionsError.java
index a862aafb05..a073cbb6ad 100644
--- a/assertj-core/src/main/java/org/assertj/core/error/MultipleAssertionsError.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/MultipleAssertionsError.java
@@ -15,29 +15,38 @@
  */
 package org.assertj.core.error;
 
-import static java.util.stream.Collectors.toList;
-import static org.assertj.core.error.AssertionErrorMessagesAggregator.aggregateErrorMessages;
-
 import java.io.Serial;
 import java.util.List;
 
 import org.assertj.core.description.Description;
+import org.assertj.core.presentation.StandardRepresentation;
 
 public class MultipleAssertionsError extends AssertionError {
 
   @Serial
   private static final long serialVersionUID = -5547434453993413952L;
 
-  private final List errors;
+  private final Description description;
+  private final Object objectUnderTest;
+  private final List errors;
 
-  public MultipleAssertionsError(Description description, Object objectUnderTest, List errors) {
-    super(formatDescription(description) + "%n".formatted() + describesObjectUnderTest(objectUnderTest) + ","
-          + createMessage(errors));
+  public MultipleAssertionsError(Description description, Object objectUnderTest, List errors) {
+    this.description = description;
+    this.objectUnderTest = objectUnderTest;
     this.errors = errors;
   }
 
-  private static String describesObjectUnderTest(Object objectUnderTest) {
-    return "For %s".formatted(objectUnderTest);
+  @Override
+  public String getMessage() {
+    return StandardRepresentation.STANDARD_REPRESENTATION.toStringOf(this);
+  }
+
+  public Object getObjectUnderTest() {
+    return objectUnderTest;
+  }
+
+  public Description getDescription() {
+    return description;
   }
 
   /**
@@ -45,19 +54,7 @@ private static String describesObjectUnderTest(Object objectUnderTest) {
    *
    * @return the list of errors
    */
-  public List getErrors() {
+  public List getErrors() {
     return errors;
   }
-
-  private static String formatDescription(Description description) {
-    return DescriptionFormatter.instance().format(description);
-  }
-
-  private static String createMessage(List errors) {
-    List errorsMessage = errors.stream()
-                                       .map(AssertionError::getMessage)
-                                       .collect(toList());
-    return aggregateErrorMessages(errorsMessage);
-  }
-
 }
diff --git a/assertj-core/src/main/java/org/assertj/core/error/ShouldBeEqual.java b/assertj-core/src/main/java/org/assertj/core/error/ShouldBeEqual.java
index f110a31dea..f1d5f83eef 100644
--- a/assertj-core/src/main/java/org/assertj/core/error/ShouldBeEqual.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/ShouldBeEqual.java
@@ -55,7 +55,7 @@ public class ShouldBeEqual {
   protected final MessageFormatter messageFormatter = MessageFormatter.instance();
   protected final ComparisonStrategy comparisonStrategy;
   private final Representation representation;
-  private final ConstructorInvoker constructorInvoker = new ConstructorInvoker();
+  private ConstructorInvoker constructorInvoker = new ConstructorInvoker();
 
   /**
    * Creates a new {@link ShouldBeEqual}.
diff --git a/assertj-core/src/main/java/org/assertj/core/error/ShouldContainEntry.java b/assertj-core/src/main/java/org/assertj/core/error/ShouldContainEntry.java
index a4453d505f..5e12be0d00 100644
--- a/assertj-core/src/main/java/org/assertj/core/error/ShouldContainEntry.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/ShouldContainEntry.java
@@ -20,7 +20,7 @@
 import org.assertj.core.api.Condition;
 
 /**
- * Creates an error message indicating that an assertion that verifies a map contains an entry..
+ * Creates an error message indicating that an assertion that verifies a map contains an entry.
  */
 public class ShouldContainEntry extends BasicErrorMessageFactory {
 
diff --git a/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKey.java b/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKey.java
index 2bda4681e5..bbbc8bb309 100644
--- a/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKey.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKey.java
@@ -18,7 +18,7 @@
 import org.assertj.core.api.Condition;
 
 /**
- * Creates an error message indicating that an assertion that verifies a map contains a key..
+ * Creates an error message indicating that an assertion that verifies a map contains a key.
  */
 public class ShouldContainKey extends BasicErrorMessageFactory {
 
diff --git a/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKeys.java b/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKeys.java
index d589637836..241f408903 100644
--- a/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKeys.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/ShouldContainKeys.java
@@ -18,8 +18,8 @@
 import java.util.Set;
 
 /**
- * Creates an error message indicating that an assertion that verifies a map contains a key..
- * 
+ * Creates an error message indicating that an assertion that verifies a map contains a key.
+ *
  * @author Nicolas François
  * @author Joel Costigliola
  */
@@ -27,7 +27,7 @@ public class ShouldContainKeys extends BasicErrorMessageFactory {
 
   /**
    * Creates a new {@link ShouldContainKeys}.
-   * 
+   *
    * @param  key type
    * @param actual the actual value in the failed assertion.
    * @param keys the expected keys
diff --git a/assertj-core/src/main/java/org/assertj/core/groups/FieldsOrPropertiesExtractor.java b/assertj-core/src/main/java/org/assertj/core/groups/FieldsOrPropertiesExtractor.java
index 60670e4249..03a2a70448 100644
--- a/assertj-core/src/main/java/org/assertj/core/groups/FieldsOrPropertiesExtractor.java
+++ b/assertj-core/src/main/java/org/assertj/core/groups/FieldsOrPropertiesExtractor.java
@@ -15,7 +15,6 @@
  */
 package org.assertj.core.groups;
 
-import static java.util.stream.Collectors.toList;
 import static org.assertj.core.util.IterableUtil.toArray;
 import static org.assertj.core.util.Lists.newArrayList;
 import static org.assertj.core.util.Streams.stream;
@@ -64,7 +63,7 @@ public static  T[] extract(F[] objects, Function extractor)
    */
   public static  List extract(Iterable objects, Function extractor) {
     checkObjectToExtractFromIsNotNull(objects);
-    return stream(objects).map(extractor).collect(toList());
+    return stream(objects).map(extractor).toList();
   }
 
   private static void checkObjectToExtractFromIsNotNull(Object object) {
diff --git a/assertj-core/src/main/java/org/assertj/core/presentation/StandardRepresentation.java b/assertj-core/src/main/java/org/assertj/core/presentation/StandardRepresentation.java
index 525257c333..b464942873 100644
--- a/assertj-core/src/main/java/org/assertj/core/presentation/StandardRepresentation.java
+++ b/assertj-core/src/main/java/org/assertj/core/presentation/StandardRepresentation.java
@@ -26,6 +26,7 @@
 import static org.assertj.core.util.Preconditions.checkArgument;
 import static org.assertj.core.util.Strings.concat;
 import static org.assertj.core.util.Strings.quote;
+import static org.assertj.core.util.Throwables.describeErrors;
 import static org.assertj.core.util.Throwables.getStackTrace;
 
 import java.io.File;
@@ -70,6 +71,8 @@
 import org.assertj.core.configuration.Configuration;
 import org.assertj.core.configuration.ConfigurationProvider;
 import org.assertj.core.data.MapEntry;
+import org.assertj.core.error.DescriptionFormatter;
+import org.assertj.core.error.MultipleAssertionsError;
 import org.assertj.core.groups.Tuple;
 import org.assertj.core.util.Closeables;
 import org.assertj.core.util.diff.ChangeDelta;
@@ -85,6 +88,7 @@
 public class StandardRepresentation implements Representation {
 
   private static final String NULL = "null";
+  private static final String LINE_SEPARATOR = System.lineSeparator();
 
   // can share this as StandardRepresentation has no state
   public static final StandardRepresentation STANDARD_REPRESENTATION = new StandardRepresentation();
@@ -261,6 +265,7 @@ public String toStringOf(Object object) {
     if (object instanceof AtomicBoolean atomicBoolean) return toStringOf(atomicBoolean);
     if (object instanceof AtomicLong atomicLong) return toStringOf(atomicLong);
     if (object instanceof Number number) return toStringOf(number);
+    if (object instanceof MultipleAssertionsError multipleAssertionsError) return toStringOf(multipleAssertionsError);
     if (object instanceof Throwable throwable) return toStringOf(throwable);
     return fallbackToStringOf(object);
   }
@@ -539,6 +544,36 @@ protected String toStringOf(Map map) {
     }
   }
 
+  protected String toStringOf(MultipleAssertionsError multipleAssertionsError) {
+    List assertionErrors = multipleAssertionsError.getErrors();
+    int errorsCount = assertionErrors.size();
+
+    if (errorsCount == 0) return multipleAssertionsError.getMessage();
+
+    String formattedDescription = DescriptionFormatter.instance().format(multipleAssertionsError.getDescription());
+    formattedDescription = formattedDescription.isEmpty() ? "" : "%s%n".formatted(formattedDescription);
+
+    Object objectUnderTest = multipleAssertionsError.getObjectUnderTest();
+    String objectUnderTestDescription = objectUnderTest != null ? " for: %s".formatted(objectUnderTest) : ":";
+    var builder = new StringBuilder(formattedDescription).append(errorsCount)
+                                                         .append(" assertion error")
+                                                         .append(errorsCount == 1 ? "" : "s")
+                                                         .append(objectUnderTestDescription)
+                                                         .append(LINE_SEPARATOR);
+    List errorDescriptions = describeErrors(assertionErrors);
+    String errorSeparator = LINE_SEPARATOR + "-- error %d --";
+
+    for (int i = 0; i < errorsCount; i++) {
+      builder.append(errorSeparator.formatted(i + 1));
+      String message = errorDescriptions.get(i);
+      // add a line before for readability unless the message has already one
+      if (!message.startsWith(LINE_SEPARATOR)) builder.append(LINE_SEPARATOR);
+      builder.append(message);
+    }
+
+    return builder.toString();
+  }
+
   protected String toStringOf(Throwable throwable) {
     StackTraceElement[] elements = throwable.getStackTrace();
     // if the line limit is 0, we assume the user don't want to print stack trace
diff --git a/assertj-core/src/main/java/org/assertj/core/util/Throwables.java b/assertj-core/src/main/java/org/assertj/core/util/Throwables.java
index 28eff73f65..fd6e196a63 100644
--- a/assertj-core/src/main/java/org/assertj/core/util/Throwables.java
+++ b/assertj-core/src/main/java/org/assertj/core/util/Throwables.java
@@ -34,6 +34,7 @@
 import java.util.stream.Stream;
 
 import org.assertj.core.api.ThrowableAssert;
+import org.assertj.core.presentation.StandardRepresentation;
 import org.assertj.core.util.introspection.IntrospectionError;
 
 /**
@@ -51,21 +52,53 @@ public final class Throwables {
   private Throwables() {}
 
   private static final Function ERROR_DESCRIPTION_EXTRACTOR = throwable -> {
+    int maxStackTraceElements = StandardRepresentation.getMaxStackTraceElementsDisplayed();
     Throwable cause = throwable.getCause();
-    if (cause == null) return throwable.getMessage();
+    if (cause == null) {
+      if (maxStackTraceElements == 0) return throwable.getMessage();
+      StackTraceElement[] stackTrace = throwable.getStackTrace();
+      String info = "";
+      if (maxStackTraceElements < stackTrace.length) {
+        info = (stackTrace.length - maxStackTraceElements)
+               + " remaining lines not displayed - this can be changed with Assertions.setMaxStackTraceElementsDisplayed)";
+      }
+
+      return format("%s%n" +
+                    "first %d stack trace %s:%n" +
+                    "%s%s%n",
+                    throwable.getMessage(),
+                    maxStackTraceElements,
+                    maxStackTraceElements == 1 ? "element" : "elements",
+                    getStackTraceDescription(stackTrace, maxStackTraceElements),
+                    info);
+    }
     // error has a cause, display the cause message and the first stack trace elements.
-    String stackTraceDescription = stream(cause.getStackTrace()).limit(5)
-                                                                .map(stackTraceElement -> "\tat %s%n".formatted(stackTraceElement))
-                                                                .collect(joining());
+    if (maxStackTraceElements == 0)
+      return format("%s%ncause message: %s%n", throwable.getMessage(), cause.getMessage());
+
     return format("%s%n" +
                   "cause message: %s%n" +
-                  "cause first five stack trace elements:%n" +
+                  "cause first %d stack trace %s:%n" +
                   "%s",
                   throwable.getMessage(),
                   cause.getMessage(),
-                  stackTraceDescription);
+                  maxStackTraceElements,
+                  maxStackTraceElements == 1 ? "element" : "elements",
+                  getStackTraceDescription(cause.getStackTrace(), maxStackTraceElements));
   };
 
+  private static String getStackTraceDescription(StackTraceElement[] causeStackTrace, int maxStackTraceElements) {
+    return stream(causeStackTrace).skip(getFirstStackTraceElementFromTestIndex(causeStackTrace))
+                                  .limit(maxStackTraceElements)
+                                  .map("\tat %s%n"::formatted)
+                                  .collect(joining());
+  }
+
+  private static int getFirstStackTraceElementFromTestIndex(StackTraceElement[] stackTrace) {
+    var firstStackTraceElementFromTest = getFirstStackTraceElementFromTest(stackTrace);
+    return firstStackTraceElementFromTest == null ? 0 : List.of(stackTrace).indexOf(firstStackTraceElementFromTest);
+  }
+
   public static List describeErrors(List errors) {
     return extract(errors, ERROR_DESCRIPTION_EXTRACTOR);
   }
@@ -88,7 +121,7 @@ public static Throwable catchThrowable(ThrowableAssert.ThrowingCallable shouldRa
   /**
    * Appends the stack trace of the current thread to the one in the given {@link Throwable}.
    *
-   * @param t the given {@code Throwable}.
+   * @param t                 the given {@code Throwable}.
    * @param methodToStartFrom the name of the method used as the starting point of the current thread's stack trace.
    */
   public static void appendStackTraceInCurrentThreadToThrowable(Throwable t, String methodToStartFrom) {
@@ -121,28 +154,28 @@ private static List stackTraceInCurrentThread() {
    * 

* Therefore, instead of seeing this: *

 --------------- stack trace not filtered -----------------
-   org.opentest4j.AssertionFailedError:
-   expected: "messi"
-   but was: "ronaldo"
-  
-   at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
-   at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
-   at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
-   at org.assertj.core/org.assertj.core.error.ConstructorInvoker.newInstance(ConstructorInvoker.java:28)
-   at org.assertj.core/org.assertj.core.error.ShouldBeEqual.assertionFailedError(ShouldBeEqual.java:208)
-   at org.assertj.core/org.assertj.core.error.ShouldBeEqual.toAssertionError(ShouldBeEqual.java:113)
-   at org.assertj.core/org.assertj.core.internal.Failures.failure(Failures.java:88)
-   at org.assertj.core/org.assertj.core.internal.Objects.assertEqual(Objects.java:214)
-  
-   --------------- stack trace filtered -----------------
-   org.opentest4j.AssertionFailedError:
-   expected: "messi"
-   but was: "ronaldo"
-  
-   at java.base/java.lang.reflect.Method.invoke(Method.java:580)
-   at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
-   at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
- + * org.opentest4j.AssertionFailedError: + * expected: "messi" + * but was: "ronaldo" + * + * at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) + * at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) + * at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486) + * at org.assertj.core/org.assertj.core.error.ConstructorInvoker.newInstance(ConstructorInvoker.java:28) + * at org.assertj.core/org.assertj.core.error.ShouldBeEqual.assertionFailedError(ShouldBeEqual.java:208) + * at org.assertj.core/org.assertj.core.error.ShouldBeEqual.toAssertionError(ShouldBeEqual.java:113) + * at org.assertj.core/org.assertj.core.internal.Failures.failure(Failures.java:88) + * at org.assertj.core/org.assertj.core.internal.Objects.assertEqual(Objects.java:214) + * + * --------------- stack trace filtered ----------------- + * org.opentest4j.AssertionFailedError: + * expected: "messi" + * but was: "ronaldo" + * + * at java.base/java.lang.reflect.Method.invoke(Method.java:580) + * at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) + * at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
+ * * @param throwable the {@code Throwable} to filter stack trace. */ public static void removeAssertJRelatedElementsFromStackTrace(Throwable throwable) { @@ -253,7 +286,8 @@ private static boolean isProxiedAssertionClass(String className) { } private static T addLineNumberToErrorMessage(T error) { - StackTraceElement testStackTraceElement = Throwables.getFirstStackTraceElementFromTest(error.getStackTrace()); + StackTraceElement[] stackTrace = error.getStackTrace(); + StackTraceElement testStackTraceElement = Throwables.getFirstStackTraceElementFromTest(stackTrace); if (testStackTraceElement != null) { try { return createNewInstanceWithLineNumberInErrorMessage(error, testStackTraceElement); @@ -272,14 +306,10 @@ private static T createNewInstanceWithLineNumberInErrorMes return errorWithLineNumber; } - private static boolean isOpentest4jAssertionFailedError(T error) { - return isInstanceOf(error, "org.opentest4j.AssertionFailedError"); - } - - private static boolean isInstanceOf(Object object, String className) { + private static boolean isOpentest4jAssertionFailedError(Throwable error) { try { - Class type = Class.forName(className); - return type.isInstance(object); + Class type = Class.forName("org.opentest4j.AssertionFailedError"); + return type.isInstance(error); } catch (ClassNotFoundException e) { return false; } diff --git a/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java b/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java index 5040774243..93633a06f0 100644 --- a/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java +++ b/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableBDDSoftAssertionsTest.java @@ -16,7 +16,6 @@ package org.assertj.core.api; import static java.lang.String.format; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.testkit.ErrorMessagesForTest.shouldBeEqualMessage; @@ -36,11 +35,12 @@ import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; + import org.assertj.core.data.MapEntry; +import org.assertj.core.error.MultipleAssertionsError; import org.assertj.core.internal.OffsetDateTimeByInstantComparator; import org.assertj.core.util.Lists; import org.junit.jupiter.api.Test; -import org.opentest4j.MultipleFailuresError; class AutoCloseableBDDSoftAssertionsTest { @@ -146,8 +146,8 @@ public String toString() { softly.then(OffsetTime.of(12, 0, 0, 0, ZoneOffset.UTC)).isEqualTo(OffsetTime.of(13, 0, 0, 0, ZoneOffset.UTC)); softly.then(OffsetDateTime.MIN).isEqualTo(OffsetDateTime.MAX); - } catch (MultipleFailuresError e) { - List errors = e.getFailures().stream().map(Object::toString).collect(toList()); + } catch (MultipleAssertionsError e) { + List errors = e.getErrors().stream().map(Object::toString).toList(); assertThat(errors).hasSize(45); assertThat(errors.get(0)).contains(shouldBeEqualMessage("0", "1")); diff --git a/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java b/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java index c6b67af50b..88857c822e 100644 --- a/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java +++ b/assertj-core/src/test/java/org/assertj/core/api/AutoCloseableSoftAssertionsTest.java @@ -17,7 +17,6 @@ import static java.lang.String.format; import static java.time.ZoneOffset.UTC; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.data.MapEntry.entry; @@ -41,11 +40,12 @@ import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; + +import org.assertj.core.error.MultipleAssertionsError; import org.assertj.core.internal.ChronoLocalDateTimeComparator; import org.assertj.core.internal.ChronoZonedDateTimeByInstantComparator; import org.assertj.core.internal.OffsetDateTimeByInstantComparator; import org.junit.jupiter.api.Test; -import org.opentest4j.MultipleFailuresError; class AutoCloseableSoftAssertionsTest { @@ -161,8 +161,8 @@ public String toString() { .isEqualTo(OffsetTime.of(13, 0, 0, 0, ZoneOffset.UTC)); softly.assertThat(OffsetDateTime.MIN).isEqualTo(OffsetDateTime.MAX); - } catch (MultipleFailuresError e) { - List errors = e.getFailures().stream().map(Object::toString).collect(toList()); + } catch (MultipleAssertionsError e) { + List errors = e.getErrors().stream().map(Object::toString).toList(); assertThat(errors).hasSize(49); assertThat(errors.get(0)).contains(shouldBeEqualMessage("0", "1").formatted()); diff --git a/assertj-core/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java b/assertj-core/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java index 04ae61424e..148402b145 100644 --- a/assertj-core/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java +++ b/assertj-core/src/test/java/org/assertj/core/api/BDDSoftAssertionsTest.java @@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyList; import static java.util.Spliterators.emptySpliterator; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -98,6 +97,7 @@ import org.assertj.core.api.iterable.ThrowingExtractor; import org.assertj.core.api.test.ComparableExample; import org.assertj.core.data.MapEntry; +import org.assertj.core.error.MultipleAssertionsError; import org.assertj.core.testkit.Animal; import org.assertj.core.testkit.CartoonCharacter; import org.assertj.core.testkit.Name; @@ -106,7 +106,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.opentest4j.MultipleFailuresError; class BDDSoftAssertionsTest extends BaseAssertionsTest { @@ -281,9 +280,9 @@ public String toString() { softly.then(emptySpliterator()).withFailMessage("Spliterator check").hasCharacteristics(123); softly.then(new LongAdder()).withFailMessage("LongAdder check").hasValue(123L); // WHEN - MultipleFailuresError error = catchThrowableOfType(MultipleFailuresError.class, () -> softly.assertAll()); + var error = catchThrowableOfType(MultipleAssertionsError.class, () -> softly.assertAll()); // THEN - List errors = error.getFailures().stream().map(Object::toString).collect(toList()); + List errors = error.getErrors().stream().map(Object::toString).toList(); assertThat(errors).hasSize(61); assertThat(errors.get(0)).contains(shouldBeEqualMessage("0", "1")); assertThat(errors.get(1)).contains("%nExpecting value to be true but was false".formatted()); @@ -361,8 +360,8 @@ public String toString() { + " %n" + "not to have a port but had:%n" + " <80>")); - assertThat(errors.get(52)).contains(format("does-not-exist")); - assertThat(errors.get(53)).contains(format("2000")); + assertThat(errors.get(52)).contains("does-not-exist"); + assertThat(errors.get(53)).contains("2000"); assertThat(errors.get(54)).contains("duration check"); assertThat(errors.get(55)).contains("instant check"); assertThat(errors.get(56)).contains("ZonedDateTime check"); diff --git a/assertj-core/src/test/java/org/assertj/core/api/SoftAssertionsTest.java b/assertj-core/src/test/java/org/assertj/core/api/SoftAssertionsTest.java index a80ef7106d..46671ad2df 100644 --- a/assertj-core/src/test/java/org/assertj/core/api/SoftAssertionsTest.java +++ b/assertj-core/src/test/java/org/assertj/core/api/SoftAssertionsTest.java @@ -20,7 +20,6 @@ import static java.time.temporal.ChronoUnit.HOURS; import static java.time.temporal.ChronoUnit.SECONDS; import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -104,6 +103,7 @@ import org.assertj.core.api.iterable.ThrowingExtractor; import org.assertj.core.api.test.ComparableExample; import org.assertj.core.data.MapEntry; +import org.assertj.core.error.MultipleAssertionsError; import org.assertj.core.testkit.Animal; import org.assertj.core.testkit.CartoonCharacter; import org.assertj.core.testkit.CaseInsensitiveStringComparator; @@ -118,7 +118,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.opentest4j.MultipleFailuresError; /** * Tests for {@link SoftAssertions}. @@ -310,8 +309,8 @@ public String toString() { softly.assertAll(); fail("Should not reach here"); - } catch (MultipleFailuresError e) { - List errors = e.getFailures().stream().map(Object::toString).collect(toList()); + } catch (MultipleAssertionsError e) { + List errors = e.getErrors().stream().map(Object::toString).toList(); then(errors).hasSize(54); then(errors.get(0)).contains(shouldBeEqualMessage("0", "1")); then(errors.get(1)).contains("%nExpecting value to be true but was false".formatted()); diff --git a/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asHexString_Test.java b/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asHexString_Test.java index 5816778ca0..6f00ab5519 100644 --- a/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asHexString_Test.java +++ b/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asHexString_Test.java @@ -18,29 +18,22 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.testkit.ErrorMessagesForTest.shouldBeEqualMessage; import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import static org.assertj.core.util.AssertionsUtil.expectAssumptionNotMetException; import static org.assertj.core.util.FailureMessages.actualIsNull; import org.assertj.core.api.SoftAssertions; -import org.assertj.core.error.AssertJMultipleFailuresError; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; -/** - * Tests for {@link org.assertj.core.api.ByteArrayAssert#asHexString()}. - */ -@DisplayName("ByteArrayAssert asHexString") class ByteArrayAssert_asHexString_Test { private static final byte[] BYTES = new byte[] { -1, 0, 1 }; @Test void should_pass() { - // GIVEN - // WHEN / THEN assertThat(BYTES).asHexString() .startsWith("FF") .isEqualTo("FF0001"); @@ -53,8 +46,8 @@ void should_fail_if_actual_does_not_match() { // WHEN var assertionError = expectAssertionError(() -> assertThat(actual).asHexString().isEqualTo("010203")); // THEN - assertThat(assertionError).hasMessage(shouldBeEqualMessage("\"FF0001\"", "\"010203\"")) - .isExactlyInstanceOf(AssertionFailedError.class); + then(assertionError).hasMessage(shouldBeEqualMessage("\"FF0001\"", "\"010203\"")) + .isExactlyInstanceOf(AssertionFailedError.class); } @Test @@ -64,7 +57,7 @@ void should_fail_if_actual_is_null() { // WHEN var error = expectAssertionError(() -> assertThat(bytes).asHexString()); // THEN - assertThat(error).hasMessage(actualIsNull()); + then(error).hasMessage(actualIsNull()); } @Test @@ -87,12 +80,11 @@ void should_fail_with_soft_assertions_capturing_all_errors() { .isBlank(); var assertionError = expectAssertionError(softly::assertAll); // THEN - assertThat(assertionError).hasMessageContainingAll("Multiple Failures (2 failures)", - "-- failure 1 --", - shouldBeEqualMessage("\"FF0001\"", "\"010203\""), - "-- failure 2 --", - "Expecting blank but was: \"FF0001\"") - .isExactlyInstanceOf(AssertJMultipleFailuresError.class); + then(assertionError).hasMessageStartingWith("2 assertion errors:") + .hasMessageContainingAll("-- error 1 --", + shouldBeEqualMessage("\"FF0001\"", "\"010203\""), + "-- error 2 --", + "Expecting blank but was: \"FF0001\""); } @Test diff --git a/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asString_Test.java b/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asString_Test.java index bd537a2210..0bcea6f2c9 100644 --- a/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asString_Test.java +++ b/assertj-core/src/test/java/org/assertj/core/api/bytearray/ByteArrayAssert_asString_Test.java @@ -24,7 +24,6 @@ import static org.assertj.core.util.FailureMessages.actualIsNull; import org.assertj.core.api.SoftAssertions; -import org.assertj.core.error.AssertJMultipleFailuresError; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; @@ -82,12 +81,11 @@ void should_fail_with_soft_assertions_capturing_all_errors() { .isBlank(); var assertionError = expectAssertionError(softly::assertAll); // THEN - assertThat(assertionError).hasMessageContainingAll("Multiple Failures (2 failures)", - "-- failure 1 --", + assertThat(assertionError).hasMessageContainingAll("2 assertion errors", + "-- error 1 --", shouldBeEqualMessage("\"foo\"", "\"bar\""), - "-- failure 2 --", - "Expecting blank but was: \"foo\"") - .isExactlyInstanceOf(AssertJMultipleFailuresError.class); + "-- error 2 --", + "Expecting blank but was: \"foo\""); } @Test diff --git a/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleAssertionsError_Test.java b/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleAssertionsError_Test.java index 8245a8fdd4..82fa7da385 100644 --- a/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleAssertionsError_Test.java +++ b/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleAssertionsError_Test.java @@ -15,83 +15,33 @@ */ package org.assertj.core.error; -import static java.lang.String.format; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.util.Lists.list; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; import java.util.List; import org.assertj.core.description.Description; import org.assertj.core.internal.TestDescription; +import org.assertj.core.presentation.StandardRepresentation; import org.junit.jupiter.api.Test; -import org.opentest4j.MultipleFailuresError; class AssertionErrorCreator_multipleAssertionsError_Test { - record Bar(int id) { - } - - public record Foo(String id, Bar bar) { - } - private final AssertionErrorCreator assertionErrorCreator = new AssertionErrorCreator(); @Test - void should_create_MultipleFailuresError_using_reflection() { + void should_create_MultipleAssertionsError() { // GIVEN Description description = new TestDescription("description"); - List errors = list(new AssertionError("error1"), new AssertionError("error2")); + AssertionError error1 = new AssertionError("error1"); + AssertionError error2 = new AssertionError("error2"); + List errors = list(error1, error2); // WHEN - AssertionError assertionError = assertionErrorCreator.multipleAssertionsError(description, null, errors); + var assertionError = assertionErrorCreator.multipleAssertionsError(description, null, errors); // THEN - then(assertionError).isInstanceOf(MultipleFailuresError.class) - .hasMessageContainingAll("[description]", - "(2 failures)", - "error1", - "error2"); - then(((MultipleFailuresError) assertionError).getFailures()).containsExactlyElementsOf(errors); + then(assertionError).isInstanceOf(MultipleAssertionsError.class) + .hasMessage(StandardRepresentation.STANDARD_REPRESENTATION.toStringOf(assertionError)); + then(((MultipleAssertionsError) assertionError).getErrors()).containsExactly(error1, error2); } - @Test - void should_create_MultipleAssertionsError_when_MultipleFailuresError_could_not_be_created_and_actual_root_instance_is_not_null() throws Exception { - // GIVEN - Object actualRootInstance = new Foo("1", new Bar(1)); - Description description = new TestDescription("description"); - List errors = list(new AssertionError("error1"), new AssertionError("error2")); - ConstructorInvoker constructorInvoker = mock(ConstructorInvoker.class); - given(constructorInvoker.newInstance(anyString(), any(Class[].class), any(Object[].class))).willThrow(Exception.class); - assertionErrorCreator.constructorInvoker = constructorInvoker; - // WHEN - AssertionError assertionError = assertionErrorCreator.multipleAssertionsError(description, actualRootInstance, errors); - // THEN - then(assertionError).isNotInstanceOf(MultipleFailuresError.class) - .hasMessage(format("[description] %n" + - "For Foo[id=1, bar=Bar[id=1]],%n" + - "The following 2 assertions failed:%n" + - "1) error1%n" + - "2) error2%n")); - } - - @Test - void should_create_MultipleAssertionsError_when_MultipleFailuresError_could_not_be_created_and_actual_root_instance_is_null() throws Exception { - // GIVEN - Description description = new TestDescription("description"); - List errors = list(new AssertionError("error1"), new AssertionError("error2")); - ConstructorInvoker constructorInvoker = mock(ConstructorInvoker.class); - given(constructorInvoker.newInstance(anyString(), any(Class[].class), any(Object[].class))).willThrow(Exception.class); - assertionErrorCreator.constructorInvoker = constructorInvoker; - // WHEN - AssertionError assertionError = assertionErrorCreator.multipleAssertionsError(description, null, errors); - // THEN - then(assertionError).isNotInstanceOf(MultipleFailuresError.class) - .hasMessage(format("[description] %n" + - "For null,%n" + - "The following 2 assertions failed:%n" + - "1) error1%n" + - "2) error2%n")); - } } diff --git a/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleSoftAssertionsError_Test.java b/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleSoftAssertionsError_Test.java deleted file mode 100644 index 9cf2177af5..0000000000 --- a/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_multipleSoftAssertionsError_Test.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2012-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.assertj.core.error; - -import static java.lang.String.format; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.util.Lists.list; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -import java.util.List; -import org.assertj.core.api.SoftAssertionError; -import org.junit.jupiter.api.Test; -import org.opentest4j.MultipleFailuresError; - -class AssertionErrorCreator_multipleSoftAssertionsError_Test { - - private AssertionErrorCreator assertionErrorCreator = new AssertionErrorCreator(); - - @Test - void should_create_AssertJMultipleFailuresError_using_reflection() { - // GIVEN - List errors = list(new AssertionError("%nerror1".formatted()), new AssertionError("%nerror2".formatted())); - // WHEN - AssertionError assertionError = assertionErrorCreator.multipleSoftAssertionsError(errors); - // THEN - then(assertionError).isInstanceOf(AssertJMultipleFailuresError.class) - .hasMessage(format("%nMultiple Failures (2 failures)%n" + - "-- failure 1 --%n" + - "error1%n" + - "-- failure 2 --%n" + - "error2")); - MultipleFailuresError assertionFailedError = (MultipleFailuresError) assertionError; - then(assertionFailedError.getFailures()).containsExactlyElementsOf(errors); - } - - @Test - void should_create_SoftAssertionError_when_MultipleFailuresError_could_not_be_created() throws Exception { - // GIVEN - List errors = list(new AssertionError("error1"), new AssertionError("error2")); - ConstructorInvoker constructorInvoker = mock(ConstructorInvoker.class); - given(constructorInvoker.newInstance(anyString(), any(Class[].class), any(Object[].class))).willThrow(Exception.class); - assertionErrorCreator.constructorInvoker = constructorInvoker; - // WHEN - AssertionError assertionError = assertionErrorCreator.multipleSoftAssertionsError(errors); - // THEN - then(assertionError).isNotInstanceOf(MultipleFailuresError.class) - .isInstanceOf(SoftAssertionError.class) - .hasMessage(format("%n" - + "The following 2 assertions failed:%n" - + "1) error1%n" - + "2) error2%n")); - } -} diff --git a/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_tryThrowingMultipleFailuresError_Test.java b/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_tryThrowingMultipleFailuresError_Test.java deleted file mode 100644 index 7689baa71e..0000000000 --- a/assertj-core/src/test/java/org/assertj/core/error/AssertionErrorCreator_tryThrowingMultipleFailuresError_Test.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.assertj.core.error; - -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.api.BDDAssertions.thenCode; -import static org.assertj.core.util.Lists.list; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -import java.util.List; -import org.junit.jupiter.api.Test; -import org.opentest4j.MultipleFailuresError; - -class AssertionErrorCreator_tryThrowingMultipleFailuresError_Test { - - private AssertionErrorCreator assertionErrorCreator = new AssertionErrorCreator(); - - @Test - void should_throw_MultipleFailuresError() { - // GIVEN - List errors = list(new AssertionError("%nerror1".formatted()), new AssertionError("%nerror2".formatted())); - // WHEN - Throwable thrown = catchThrowable(() -> assertionErrorCreator.tryThrowingMultipleFailuresError(errors)); - // THEN - then(thrown).isInstanceOf(MultipleFailuresError.class) - .hasMessage(format("%nMultiple Failures (2 failures)%n" + - "-- failure 1 --%n" + - "error1%n" + - "-- failure 2 --%n" + - "error2")); - MultipleFailuresError assertionFailedError = (MultipleFailuresError) thrown; - then(assertionFailedError.getFailures()).containsExactlyElementsOf(errors); - } - - @Test - void should_not_throw_MultipleFailuresError_when_failing_to_create_it() throws Exception { - // GIVEN - List errors = list(new AssertionError("error1"), new AssertionError("error2")); - ConstructorInvoker constructorInvoker = mock(ConstructorInvoker.class); - given(constructorInvoker.newInstance(anyString(), any(Class[].class), any(Object[].class))).willThrow(Exception.class); - assertionErrorCreator.constructorInvoker = constructorInvoker; - // THEN - thenCode(() -> assertionErrorCreator.tryThrowingMultipleFailuresError(errors)).doesNotThrowAnyException(); - } -} diff --git a/assertj-core/src/test/java/org/assertj/core/testkit/ClasspathResources.java b/assertj-core/src/test/java/org/assertj/core/testkit/ClasspathResources.java index 6fbf5acdb0..bf1e8f24fd 100644 --- a/assertj-core/src/test/java/org/assertj/core/testkit/ClasspathResources.java +++ b/assertj-core/src/test/java/org/assertj/core/testkit/ClasspathResources.java @@ -15,7 +15,7 @@ */ package org.assertj.core.testkit; -import static org.junit.platform.commons.support.ReflectionSupport.findAllResourcesInPackage; +import static org.junit.platform.commons.support.ResourceSupport.findAllResourcesInPackage; import java.io.File; import java.net.MalformedURLException; @@ -23,7 +23,9 @@ import java.net.URL; import java.nio.file.Path; import java.util.List; -import org.junit.platform.commons.support.Resource; + +import org.junit.platform.commons.io.Resource; +import org.junit.platform.commons.io.ResourceFilter; public class ClasspathResources { @@ -44,7 +46,8 @@ public static URL resourceURL(String resourceName) { } public static URI resourceURI(String resourceName) { - List resources = findAllResourcesInPackage("", resource -> resource.getName().equals(resourceName)); + ResourceFilter filter = ResourceFilter.of(resource -> resource.getName().equals(resourceName)); + List resources = findAllResourcesInPackage("", filter); if (resources.size() != 1) throw new IllegalStateException("Unique resource not found: " + resources); return resources.get(0).getUri(); } diff --git a/assertj-core/src/test/java/org/assertj/core/util/Throwables_describeErrors_Test.java b/assertj-core/src/test/java/org/assertj/core/util/Throwables_describeErrors_Test.java new file mode 100644 index 0000000000..597ad0feb6 --- /dev/null +++ b/assertj-core/src/test/java/org/assertj/core/util/Throwables_describeErrors_Test.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.assertj.core.util; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.InstanceOfAssertFactories.STRING; +import static org.assertj.core.util.Throwables.describeErrors; + +import java.util.List; + +import org.assertj.core.presentation.StandardRepresentation; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link Throwables#describeErrors(List)}. + * + * @author chanwonlee + */ +class Throwables_describeErrors_Test { + + private static final int DEFAULT_MAX_STACK_TRACE_ELEMENTS = StandardRepresentation.getMaxStackTraceElementsDisplayed(); + + @AfterEach + void afterEach() { + // restore default value + StandardRepresentation.setMaxStackTraceElementsDisplayed(DEFAULT_MAX_STACK_TRACE_ELEMENTS); + } + + @Test + void should_honor_maxStackTraceElementsDisplayed_setting_when_exception_has_a_cause() { + // GIVEN + StandardRepresentation.setMaxStackTraceElementsDisplayed(10); + var cause = new RuntimeException("cause message"); + var error = new RuntimeException("error message", cause); + // WHEN + List descriptions = describeErrors(List.of(error)); + // THEN + then(descriptions).singleElement(STRING) + .contains("cause first 10 stack trace elements:"); + then(countStackTraceElements(descriptions.get(0))).isEqualTo(StandardRepresentation.getMaxStackTraceElementsDisplayed()); + } + + @Test + void should_honor_maxStackTraceElementsDisplayed_setting_when_exception_has_no_cause() { + // GIVEN + var error = new RuntimeException("error message without cause"); + // WHEN + List descriptions = describeErrors(List.of(error)); + // THEN + then(descriptions).singleElement(STRING) + .startsWith("error message without cause") + .containsSubsequence("first 3 stack trace elements:", + "should_honor_maxStackTraceElementsDisplayed_setting_when_exception_has_no_cause(Throwables_describeErrors_Test.java:60)"); + then(countStackTraceElements(descriptions.get(0))).isEqualTo(StandardRepresentation.getMaxStackTraceElementsDisplayed()); + } + + private static long countStackTraceElements(String description) { + return description.lines() + .filter(line -> line.trim().startsWith("at ")) + .count(); + } + +} diff --git a/assertj-core/src/test/java/org/example/test/AssertJMultipleFailuresError_getMessage_Test.java b/assertj-core/src/test/java/org/example/test/AssertJMultipleFailuresError_getMessage_Test.java deleted file mode 100644 index 116655101f..0000000000 --- a/assertj-core/src/test/java/org/example/test/AssertJMultipleFailuresError_getMessage_Test.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2012-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.example.test; - -import static java.lang.String.format; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.testkit.ErrorMessagesForTest.shouldBeEqualMessage; -import static org.assertj.core.util.AssertionsUtil.expectAssertionError; -import static org.assertj.core.util.Lists.list; - -import org.assertj.core.api.SoftAssertions; -import org.assertj.core.error.AssertJMultipleFailuresError; -import org.assertj.core.testkit.Person; -import org.junit.jupiter.api.Test; - -// this is not in an assertj package as we want to check the stack trace, and we filter the element in assertj -class AssertJMultipleFailuresError_getMessage_Test { - - @Test - void should_include_line_numbers() { - // GIVEN - var assertionError = new AssertionError("boom"); - // WHEN - var error = new AssertJMultipleFailuresError("", list(assertionError)); - // THEN - then(error).hasStackTraceContaining("AssertJMultipleFailuresError_getMessage_Test.java:35"); - } - - // also verifies that we don't add stack trace line numbers twice (in soft assertion - // DefaultAssertionErrorCollector.decorateErrorsCollected and AssertJMultipleFailuresError - @Test - void should_include_stack_trace_allowing_to_navigate_to_the_failing_test_assertion_line_in_soft_assertions_context() { - // GIVEN - SoftAssertions softly = new SoftAssertions(); - softly.assertThat(list("")).isEmpty(); - softly.assertThat("abc").as("isEmpty string").isEmpty(); - softly.assertThat("abc").isEqualTo("bcd"); - // WHEN - var error = expectAssertionError(softly::assertAll); - // THEN - // @format:off - then(error).isInstanceOf(AssertJMultipleFailuresError.class) - .hasMessage(format("%nMultiple Failures (3 failures)%n" + - "-- failure 1 --%n" + - "Expecting empty but was: [\"\"]%n" + - "at AssertJMultipleFailuresError_getMessage_Test.should_include_stack_trace_allowing_to_navigate_to_the_failing_test_assertion_line_in_soft_assertions_context(AssertJMultipleFailuresError_getMessage_Test.java:48)%n" + - "-- failure 2 --%n" + - "[isEmpty string] %n" + - "Expecting empty but was: \"abc\"%n" + - "at AssertJMultipleFailuresError_getMessage_Test.should_include_stack_trace_allowing_to_navigate_to_the_failing_test_assertion_line_in_soft_assertions_context(AssertJMultipleFailuresError_getMessage_Test.java:49)%n" + - "-- failure 3 --" - + shouldBeEqualMessage("\"abc\"", "\"bcd\"") + "%n" + - "at AssertJMultipleFailuresError_getMessage_Test.should_include_stack_trace_allowing_to_navigate_to_the_failing_test_assertion_line_in_soft_assertions_context(AssertJMultipleFailuresError_getMessage_Test.java:50)")); - // @format:on - } - - @Test - void should_include_stack_trace_allowing_to_navigate_to_the_failing_test_assertion_line_in_satisfies_assertion() { - // WHEN - var error = expectAssertionError(() -> then("abc").satisfies(value -> then(list(value)).isEmpty(), - value -> then(value).as("isEmpty string").isEmpty(), - value -> then(value).isEqualTo("bcd"))); - // THEN - then(error).isInstanceOf(AssertJMultipleFailuresError.class) - .hasMessageContainingAll("AssertJMultipleFailuresError_getMessage_Test.java:73)", - "AssertJMultipleFailuresError_getMessage_Test.java:74)", - "AssertJMultipleFailuresError_getMessage_Test.java:75)"); - } - - @Test - void should_honor_description_and_show_root_object() { - // GIVEN - var assertionError = new AssertionError("%nboom".formatted()); - var error = new AssertJMultipleFailuresError("desc", new Person("tim"), list(assertionError)); - // WHEN - String message = error.getMessage(); - // THEN - then(message).startsWith(format("%n" + - "For Person[name='tim'],%n" + - "desc (1 failure)%n" + - "-- failure 1 --%n" + - "boom%n" + - "at AssertJMultipleFailuresError_getMessage_Test")); - } - - @Test - void should_honor_description() { - // GIVEN - String description = "desc"; - var error = new AssertJMultipleFailuresError(description, list(new AssertionError("boom"))); - // WHEN - String message = error.getMessage(); - // THEN - then(message).startsWith("%n%s".formatted(description)); - } - - @Test - void should_include_errors_count_and_clearly_separate_error_messages_in_soft_assertions_context() { - // GIVEN - SoftAssertions softly = new SoftAssertions(); - softly.assertThat(list("")).isEmpty(); - softly.assertThat(list("a", "b", "c")).as("isEmpty list").isEmpty(); - softly.assertThat("abc").isEmpty(); - softly.assertThat("abc").as("isEmpty string").isEmpty(); - softly.assertThat("abc").isEqualTo("bcd"); - softly.assertThat("abc").as("isEqualTo").isEqualTo("bcd"); - softly.assertThat(list("a", "b", "c")).as("contains").contains("e").doesNotContain("a"); - softly.assertThat(list("a", "b", "c")).contains("e").doesNotContain("a"); - // WHEN - var error = expectAssertionError(softly::assertAll); - // THEN - then(error).hasMessageContainingAll("%nMultiple Failures (10 failures)%n".formatted(), - "-- failure 1 --%n".formatted(), - "Expecting empty but was: [\"\"]%n".formatted(), - "-- failure 2 --%n".formatted(), - "[isEmpty list] %n".formatted(), - "Expecting empty but was: [\"a\", \"b\", \"c\"]%n".formatted(), - "-- failure 3 --%n".formatted(), - "Expecting empty but was: \"abc\"%n".formatted(), - "-- failure 4 --%n".formatted(), - "[isEmpty string] %n".formatted(), - "Expecting empty but was: \"abc\"%n".formatted(), - "-- failure 5 --%n".formatted(), - format(shouldBeEqualMessage("\"abc\"", "\"bcd\"") + "%n"), - "-- failure 6 --%n".formatted(), - format(shouldBeEqualMessage("isEqualTo", "\"abc\"", "\"bcd\"") + "%n"), - "-- failure 7 --%n".formatted(), - "[contains] %n".formatted(), - "Expecting ArrayList:%n".formatted(), - " [\"a\", \"b\", \"c\"]%n".formatted(), - "to contain:%n".formatted(), - " [\"e\"]%n".formatted(), - "but could not find the following element(s):%n".formatted(), - " [\"e\"]%n".formatted(), - "%n".formatted(), - "-- failure 8 --%n".formatted(), - "[contains] %n".formatted(), - "Expecting%n".formatted(), - " [\"a\", \"b\", \"c\"]%n".formatted(), - "not to contain%n".formatted(), - " [\"a\"]%n".formatted(), - "but found%n".formatted(), - " [\"a\"]%n".formatted(), - "%n".formatted(), - "-- failure 9 --%n".formatted(), - "Expecting ArrayList:%n".formatted(), - " [\"a\", \"b\", \"c\"]%n".formatted(), - "to contain:%n".formatted(), - " [\"e\"]%n".formatted(), - "but could not find the following element(s):%n".formatted(), - " [\"e\"]%n".formatted(), - "%n".formatted(), - "-- failure 10 --%n".formatted(), - "Expecting%n".formatted(), - " [\"a\", \"b\", \"c\"]%n".formatted(), - "not to contain%n".formatted(), - " [\"a\"]%n".formatted(), - "but found%n".formatted(), - " [\"a\"]%n".formatted()); - } - -} diff --git a/assertj-core/src/test/java/org/example/test/AutoClosableSoftAssertionsLineNumberTest.java b/assertj-core/src/test/java/org/example/test/AutoClosableSoftAssertionsLineNumberTest.java index 616442295c..ee3aa1cfaa 100644 --- a/assertj-core/src/test/java/org/example/test/AutoClosableSoftAssertionsLineNumberTest.java +++ b/assertj-core/src/test/java/org/example/test/AutoClosableSoftAssertionsLineNumberTest.java @@ -15,22 +15,21 @@ */ package org.example.test; -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import org.assertj.core.api.AutoCloseableSoftAssertions; import org.junit.jupiter.api.Test; /** - * This test has to be in a package other than org.assertj because otherwise the - * line number information will be removed by the assertj filtering of internal lines. - * {@link org.assertj.core.util.Throwables#removeAssertJRelatedElementsFromStackTrace} + * The assertions classes have to be in a package other than org.assertj to test + * the behavior of line numbers for assertions defined outside the assertj package */ class AutoClosableSoftAssertionsLineNumberTest { @Test void should_print_line_numbers_of_failed_assertions() { + // noinspection resource AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions(); softly.assertThat(1) .isLessThan(0) @@ -38,17 +37,23 @@ void should_print_line_numbers_of_failed_assertions() { // WHEN var error = expectAssertionError(softly::close); // THEN - assertThat(error).hasMessageContaining(format("%n" - + "Expecting actual:%n" - + " 1%n" - + "to be less than:%n" - + " 0 %n" - + "at AutoClosableSoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions(AutoClosableSoftAssertionsLineNumberTest.java:36)%n")) - .hasMessageContaining(format("%n" - + "Expecting actual:%n" - + " 1%n" - + "to be less than:%n" - + " 1 %n" - + "at AutoClosableSoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions(AutoClosableSoftAssertionsLineNumberTest.java:37)")); + //@format:off + then(error).hasMessageContainingAll(""" + Expecting actual: + 1 + to be less than: + 0\s + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions(AutoClosableSoftAssertionsLineNumberTest.java:35)", + """ + Expecting actual: + 1 + to be less than: + 1\s + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions(AutoClosableSoftAssertionsLineNumberTest.java:36)"); + //@format:on } } diff --git a/assertj-core/src/test/java/org/example/test/BDDSoftAssertionsLineNumberTest.java b/assertj-core/src/test/java/org/example/test/BDDSoftAssertionsLineNumberTest.java index 6c991ab6ca..1a26ca4edf 100644 --- a/assertj-core/src/test/java/org/example/test/BDDSoftAssertionsLineNumberTest.java +++ b/assertj-core/src/test/java/org/example/test/BDDSoftAssertionsLineNumberTest.java @@ -15,17 +15,15 @@ */ package org.example.test; -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import org.assertj.core.api.BDDSoftAssertions; import org.junit.jupiter.api.Test; /** - * This test has to be in a package other than org.assertj because otherwise the - * line number information will be removed by the assertj filtering of internal lines. - * {@link org.assertj.core.util.Throwables#removeAssertJRelatedElementsFromStackTrace} + * The assertions classes have to be in a package other than org.assertj to test + * the behavior of line numbers for assertions defined outside the assertj package */ class BDDSoftAssertionsLineNumberTest { @@ -38,17 +36,23 @@ void should_print_line_numbers_of_failed_assertions() { // WHEN var error = expectAssertionError(softly::assertAll); // THEN - assertThat(error).hasMessageContaining(format("%n" - + "Expecting actual:%n" - + " 1%n" - + "to be less than:%n" - + " 0 %n" - + "at BDDSoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions(BDDSoftAssertionsLineNumberTest.java:36)%n")) - .hasMessageContaining(format("%n" - + "Expecting actual:%n" - + " 1%n" - + "to be less than:%n" - + " 1 %n" - + "at BDDSoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions(BDDSoftAssertionsLineNumberTest.java:37)")); + //@format:off + then(error).hasMessageContainingAll(""" + Expecting actual: + 1 + to be less than: + 0\s + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions(BDDSoftAssertionsLineNumberTest.java:34)", + """ + Expecting actual: + 1 + to be less than: + 1\s + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions(BDDSoftAssertionsLineNumberTest.java:35)"); + //@format:on } } diff --git a/assertj-core/src/test/java/org/example/test/CustomSoftAssertionsLineNumberTest.java b/assertj-core/src/test/java/org/example/test/CustomSoftAssertionsLineNumberTest.java index 3d63492401..544d0ea07e 100644 --- a/assertj-core/src/test/java/org/example/test/CustomSoftAssertionsLineNumberTest.java +++ b/assertj-core/src/test/java/org/example/test/CustomSoftAssertionsLineNumberTest.java @@ -15,7 +15,7 @@ */ package org.example.test; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import org.junit.jupiter.api.Test; @@ -35,7 +35,7 @@ void should_print_line_numbers_of_failed_assertions_even_if_custom_assertion_in_ var error = expectAssertionError(softly::assertAll); // THEN // does not check the exact line number because it can vary (for example when Jacoco injects fields to check code coverage) - assertThat(error).hasStackTraceContaining("CustomSoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions_even_if_custom_assertion_in_non_assertj_package(CustomSoftAssertionsLineNumberTest.java:3"); + then(error).hasStackTraceContaining("should_print_line_numbers_of_failed_assertions_even_if_custom_assertion_in_non_assertj_package(CustomSoftAssertionsLineNumberTest.java:3"); } } diff --git a/assertj-core/src/test/java/org/example/test/MultipleAssertionsError_getMessage_Test.java b/assertj-core/src/test/java/org/example/test/MultipleAssertionsError_getMessage_Test.java new file mode 100644 index 0000000000..73d35ac965 --- /dev/null +++ b/assertj-core/src/test/java/org/example/test/MultipleAssertionsError_getMessage_Test.java @@ -0,0 +1,252 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example.test; + +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.util.AssertionsUtil.expectAssertionError; +import static org.assertj.core.util.Lists.list; + +import java.util.List; + +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.description.TextDescription; +import org.assertj.core.error.AssertionErrorCreator; +import org.assertj.core.error.MultipleAssertionsError; +import org.assertj.core.presentation.StandardRepresentation; +import org.assertj.core.testkit.Person; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +// this is not in an assertj package as we want to check the stack trace, and we filter the element in assertj +class MultipleAssertionsError_getMessage_Test { + + private static final int DEFAULT_MAX_STACK_TRACE_ELEMENTS = StandardRepresentation.getMaxStackTraceElementsDisplayed(); + + @AfterEach + void afterEach() { + // restore default value + StandardRepresentation.setMaxStackTraceElementsDisplayed(DEFAULT_MAX_STACK_TRACE_ELEMENTS); + } + + @Test + void should_include_stack_trace_allowing_to_navigate_to_the_failing_assertion_line_in_satisfies_assertion() { + // WHEN + var error = expectAssertionError(() -> then("abc").satisfies(value -> then(list(value)).isEmpty(), + value -> then(value).as("isEmpty string").isEmpty(), + value -> then(value).isEqualTo("bcd"))); + // THEN + then(error).isInstanceOf(MultipleAssertionsError.class) + .hasMessageContainingAll("MultipleAssertionsError_getMessage_Test.java:47)", + "MultipleAssertionsError_getMessage_Test.java:48)", + "MultipleAssertionsError_getMessage_Test.java:49)"); + } + + @Test + void should_show_description_and_object_under_test() { + // GIVEN + var assertionError = new AssertionError("boom"); + var error = new MultipleAssertionsError(new TextDescription("desc"), new Person("tim"), list(assertionError)); + // WHEN + String message = error.getMessage(); + // THEN + then(message).startsWith(""" + [desc]\s + 1 assertion error for: Person[name='tim'] + + -- error 1 -- + boom + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator())); + } + + @Test + void should_honor_description() { + // GIVEN + var error = new MultipleAssertionsError(new TextDescription("desc"), null, list(new AssertionError("boom"))); + // WHEN + String message = error.getMessage(); + // THEN + then(message).startsWith(""" + [desc]\s + 1 assertion error: + + -- error 1 -- + boom + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator())); + } + + @Test + void should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line() { + // GIVEN + SoftAssertions softly = new SoftAssertions(); + softly.assertThat(list("")).isEmpty(); + softly.assertThat(list("a", "b", "c")).as("isEmpty list").isEmpty(); + softly.assertThat("abc").isEmpty(); + softly.assertThat("abc").as("isEmpty string").isEmpty(); + softly.assertThat("abc").isEqualTo("bcd"); + softly.assertThat("abc").as("isEqualTo").isEqualTo("bcd"); + softly.assertThat(list("a", "b", "c")).as("contains").contains("e").doesNotContain("a"); + softly.assertThat(list("a", "b", "c")).contains("e").doesNotContain("a"); + // WHEN + var error = expectAssertionError(softly::assertAll); + // THEN + //@format:off + then(error).hasMessageContainingAll("10 assertion errors", + """ + + -- error 1 -- + Expecting empty but was: [""] + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:96)", + """ + + -- error 2 -- + [isEmpty list]\s + Expecting empty but was: ["a", "b", "c"] + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:97)", + """ + + -- error 3 -- + Expecting empty but was: "abc" + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:98)", + """ + + -- error 4 -- + [isEmpty string]\s + Expecting empty but was: "abc" + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:99)", + """ + + -- error 5 -- + expected: "bcd" + but was: "abc" + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:100)", + """ + + -- error 6 -- + [isEqualTo]\s + expected: "bcd" + but was: "abc" + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:101)", + """ + + -- error 7 -- + [contains]\s + Expecting ArrayList: + ["a", "b", "c"] + to contain: + ["e"] + but could not find the following element(s): + ["e"] + + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:102)", + """ + + -- error 8 -- + [contains]\s + Expecting + ["a", "b", "c"] + not to contain + ["a"] + but found + ["a"] + + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:102)", + """ + + -- error 9 -- + Expecting ArrayList: + ["a", "b", "c"] + to contain: + ["e"] + but could not find the following element(s): + ["e"] + + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:103)", + """ + + -- error 10 -- + Expecting + ["a", "b", "c"] + not to contain + ["a"] + but found + ["a"] + + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_include_errors_count_and_allow_to_navigate_to_the_failing_soft_assertion_line(MultipleAssertionsError_getMessage_Test.java:103)"); + //@format:on + } + + @Test + void should_honor_maxStackTraceElementsDisplayed_setting_in_each_assertion_error() { + // GIVEN + var assertionError1 = new AssertionError("boom"); + var assertionError2 = new AssertionError("bam"); + StandardRepresentation.setMaxStackTraceElementsDisplayed(10); + // WHEN + var error = new AssertionErrorCreator().multipleAssertionsError(List.of(assertionError1, assertionError2)); + // THEN + then(error).message().contains("first 10 stack trace elements:"); + } + + @Test + void should_show_object_under_test() { + // GIVEN + var assertionError = new AssertionError("boom"); + var error = new MultipleAssertionsError(null, new Person("tim"), list(assertionError)); + // WHEN + String message = error.getMessage(); + // THEN + then(message).startsWith(""" + 1 assertion error for: Person[name='tim'] + + -- error 1 -- + boom + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator())); + } + + @Test + void satisfies_should_include_object_under_test_in_error_message() { + // WHEN + var error = expectAssertionError(() -> then("abc").satisfies(value -> then(value).as("isEmpty string").isEmpty(), + value -> then(value).isEqualTo("bcd"))); + // THEN + then(error).isInstanceOf(MultipleAssertionsError.class) + .hasMessageContainingAll("2 assertion errors for: abc", "[isEmpty string]"); + } + +} diff --git a/assertj-core/src/test/java/org/example/test/SoftAssertionsLineNumberTest.java b/assertj-core/src/test/java/org/example/test/SoftAssertionsLineNumberTest.java index ab987a2b9f..f41b5b09dd 100644 --- a/assertj-core/src/test/java/org/example/test/SoftAssertionsLineNumberTest.java +++ b/assertj-core/src/test/java/org/example/test/SoftAssertionsLineNumberTest.java @@ -15,8 +15,7 @@ */ package org.example.test; -import static java.lang.String.format; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.core.util.AssertionsUtil.expectAssertionError; import java.util.Optional; @@ -26,9 +25,8 @@ import org.junit.jupiter.api.Test; /** - * This test has to be in a package other than org.assertj because otherwise the - * line number information will be removed by the assertj filtering of internal lines. - * {@link org.assertj.core.util.Throwables#removeAssertJRelatedElementsFromStackTrace} + * The assertions classes have to be in a package other than org.assertj to test + * the behavior of line numbers for assertions defined outside the assertj package */ class SoftAssertionsLineNumberTest { @@ -42,18 +40,24 @@ void should_print_line_numbers_of_failed_assertions() { // WHEN var error = expectAssertionError(softly::assertAll); // THEN - assertThat(error).hasMessageContaining(format("%n" - + "Expecting actual:%n" - + " 1%n" - + "to be less than:%n" - + " 0 %n" - + "at SoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions(SoftAssertionsLineNumberTest.java:40)%n")) - .hasMessageContaining(format("%n" - + "Expecting actual:%n" - + " 1%n" - + "to be less than:%n" - + " 1 %n" - + "at SoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions(SoftAssertionsLineNumberTest.java:41)")); + //@format:off + then(error).hasMessageContainingAll(""" + Expecting actual: + 1 + to be less than: + 0\s + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions(SoftAssertionsLineNumberTest.java:38)", + """ + Expecting actual: + 1 + to be less than: + 1\s + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions(SoftAssertionsLineNumberTest.java:39)"); + //@format:on } @Test @@ -68,20 +72,34 @@ void should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_ // WHEN var error = expectAssertionError(softly::assertAll); // THEN - assertThat(error).hasMessageContaining(format("%n" - + "Expecting Optional to contain:%n" - + " \"Foo\"%n" - + "but was empty.%n" - + "at SoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_calls(SoftAssertionsLineNumberTest.java:63)%n")) - .hasMessageContaining(format("%n" - + "Expecting actual not to be null%n" - + "at SoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_calls(SoftAssertionsLineNumberTest.java:65)%n")) - .hasMessageContaining(format("%n" - + "Expecting all elements of:%n" - + " [\"a\", \"b\", \"C\"]%n" - + "to match given predicate but this element did not:%n" - + " \"C\"%n" - + "at SoftAssertionsLineNumberTest.should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_calls(SoftAssertionsLineNumberTest.java:67)")); + //@format:off + then(error).hasMessageContainingAll(""" + 3 assertion errors: + + -- error 1 -- + Expecting Optional to contain: + "Foo" + but was empty. + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_calls(SoftAssertionsLineNumberTest.java:67)", + """ + + -- error 2 -- + Expecting actual not to be null + first 3 stack trace elements: + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_calls(SoftAssertionsLineNumberTest.java:69)", + """ + + -- error 3 -- + Expecting all elements of: + ["a", "b", "C"] + to match given predicate but this element did not: + "C" + """.replaceAll("\\n", System.lineSeparator()), + "should_print_line_numbers_of_failed_assertions_even_if_it_came_from_nested_calls(SoftAssertionsLineNumberTest.java:71)"); + //@format:on } } diff --git a/assertj-guava/pom.xml b/assertj-guava/pom.xml index 80d81107aa..8e70b9cf2b 100644 --- a/assertj-guava/pom.xml +++ b/assertj-guava/pom.xml @@ -152,8 +152,13 @@ https://javadoc.io/static/com.google.guava/guava/${guava.version} - https://javadoc.io/static/org.assertj/assertj-core/${project.version} + + + https://javadoc.io/static/org.assertj/assertj-core/${project.version} + ${project.basedir}/../assertj-core/target/site/apidocs + + diff --git a/assertj-guava/src/main/java/org/assertj/guava/api/TableIntegerAssert.java b/assertj-guava/src/main/java/org/assertj/guava/api/TableIntegerAssert.java index e259849632..ed146556d2 100644 --- a/assertj-guava/src/main/java/org/assertj/guava/api/TableIntegerAssert.java +++ b/assertj-guava/src/main/java/org/assertj/guava/api/TableIntegerAssert.java @@ -15,8 +15,8 @@ */ package org.assertj.guava.api; +import org.assertj.core.annotation.CheckReturnValue; import org.assertj.core.api.AbstractIntegerAssert; -import org.assertj.core.util.CheckReturnValue; /** * Assertion methods for {@link Integer}s with the possibility of returning to the {@link TableAssert}. @@ -34,4 +34,5 @@ public TableIntegerAssert(TableAssert source, Integer i) { public TableAssert returnToTable() { return source; } + } diff --git a/assertj-parent/pom.xml b/assertj-parent/pom.xml index c33f70987b..517fa18d8c 100644 --- a/assertj-parent/pom.xml +++ b/assertj-parent/pom.xml @@ -24,9 +24,9 @@ 3.0 2.20.1 - 7.1.0 + 7.2.0 0.8.14 - 0.25.0 + 0.25.1 3.9.0 3.4.0 4.9.8.2 diff --git a/assertj-tests/assertj-integration-tests/assertj-core-osgi/pom.xml b/assertj-tests/assertj-integration-tests/assertj-core-osgi/pom.xml index 0d7651b0b8..b98f27bbd7 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-osgi/pom.xml +++ b/assertj-tests/assertj-integration-tests/assertj-core-osgi/pom.xml @@ -16,6 +16,7 @@ ${project.build.directory}/${project.build.finalName}-tests.jar ${project.basedir}/../../../ false + 5.12.2 diff --git a/assertj-tests/assertj-integration-tests/assertj-core-spring-boot/pom.xml b/assertj-tests/assertj-integration-tests/assertj-core-spring-boot/pom.xml index 378f6c5e20..b2e75fce29 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-spring-boot/pom.xml +++ b/assertj-tests/assertj-integration-tests/assertj-core-spring-boot/pom.xml @@ -27,7 +27,7 @@ org.springframework.boot spring-boot-dependencies - 4.0.0 + 4.0.1 pom import diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/pom.xml b/assertj-tests/assertj-integration-tests/assertj-core-tests/pom.xml index cdf54c809d..feb8fb2c94 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/pom.xml +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/pom.xml @@ -43,7 +43,7 @@ nl.jqno.equalsverifier equalsverifier - 4.2.5 + 4.3 test diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_allOf_with_ThrowingConsumer_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_allOf_with_ThrowingConsumer_Test.java index ffb9070994..077fbb5e75 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_allOf_with_ThrowingConsumer_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_allOf_with_ThrowingConsumer_Test.java @@ -21,11 +21,11 @@ import static org.assertj.tests.core.util.AssertionsUtil.expectAssertionError; import org.assertj.core.api.ThrowingConsumer; +import org.assertj.core.error.MultipleAssertionsError; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; -import org.opentest4j.MultipleFailuresError; class Assertions_allOf_with_ThrowingConsumer_Test { @@ -48,7 +48,7 @@ void should_fail_if_any_consumer_fails(String value) { // WHEN var assertionError = expectAssertionError(() -> underTest.accept(value)); // THEN - then(assertionError).isInstanceOf(MultipleFailuresError.class); + then(assertionError).isInstanceOf(MultipleAssertionsError.class); } } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_anyOf_with_ThrowingConsumer_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_anyOf_with_ThrowingConsumer_Test.java index d362e82a2e..199ece77c8 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_anyOf_with_ThrowingConsumer_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_anyOf_with_ThrowingConsumer_Test.java @@ -21,11 +21,11 @@ import static org.assertj.tests.core.util.AssertionsUtil.expectAssertionError; import org.assertj.core.api.ThrowingConsumer; +import org.assertj.core.error.MultipleAssertionsError; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; -import org.opentest4j.MultipleFailuresError; class Assertions_anyOf_with_ThrowingConsumer_Test { @@ -48,7 +48,7 @@ void should_fail_if_all_consumers_fail() { // WHEN var assertionError = expectAssertionError(() -> underTest.accept(null)); // THEN - then(assertionError).isInstanceOf(MultipleFailuresError.class); + then(assertionError).isInstanceOf(MultipleAssertionsError.class); } } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_assertWith_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_assertWith_Test.java index fe7f2185f2..808fde583f 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_assertWith_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/Assertions_assertWith_Test.java @@ -15,7 +15,6 @@ */ package org.assertj.tests.core.api; -import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertWith; @@ -64,9 +63,7 @@ void should_include_root_object_in_failure_message() { jedi -> assertThat(jedi.lightSaberColor).isEqualTo("Red"), jedi -> assertThat(jedi.getName()).isEqualTo("Luke"))); // THEN - then(assertionError).hasMessageStartingWith(format("%n" + - "For Yoda the Jedi,%n" + - "Multiple Failures")); + then(assertionError).hasMessageStartingWith("2 assertion errors for: Yoda the Jedi"); } } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/bytearray/ByteArrayAssert_asString_with_Charset_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/bytearray/ByteArrayAssert_asString_with_Charset_Test.java index 0ba0a278f2..773247ec7f 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/bytearray/ByteArrayAssert_asString_with_Charset_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/bytearray/ByteArrayAssert_asString_with_Charset_Test.java @@ -25,7 +25,6 @@ import static org.assertj.tests.core.util.AssertionsUtil.expectAssumptionNotMetException; import org.assertj.core.api.SoftAssertions; -import org.assertj.core.error.AssertJMultipleFailuresError; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; @@ -88,12 +87,11 @@ void should_fail_with_soft_assertions_capturing_all_errors() { .isBlank(); var assertionError = expectAssertionError(softly::assertAll); // THEN - assertThat(assertionError).hasMessageContainingAll("Multiple Failures (2 failures)", - "-- failure 1 --", + assertThat(assertionError).hasMessageContainingAll("2 assertion errors:", + "-- error 1 --", shouldBeEqualMessage("\"Gerçek\"", "\"bar\""), - "-- failure 2 --", - "Expecting blank but was: \"Gerçek\"") - .isExactlyInstanceOf(AssertJMultipleFailuresError.class); + "-- error 2 --", + "Expecting blank but was: \"Gerçek\""); } @Test diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/AbstractSoftAssertionsExtensionIntegrationTests.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/AbstractSoftAssertionsExtensionIntegrationTests.java index a1463694f1..be5029f255 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/AbstractSoftAssertionsExtensionIntegrationTests.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/AbstractSoftAssertionsExtensionIntegrationTests.java @@ -23,7 +23,7 @@ import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; -import org.assertj.core.error.AssertJMultipleFailuresError; +import org.assertj.core.error.MultipleAssertionsError; import org.junit.jupiter.api.Test; import org.junit.platform.testkit.engine.EngineTestKit; @@ -75,12 +75,12 @@ private void assertExecutionResults(Class testClass, boolean nested) { // @format:off .assertThatEvents().haveExactly(nested ? 2 : 1, event(test("multipleFailures"), - finishedWithFailure(instanceOf(AssertJMultipleFailuresError.class), - message(msg -> msg.contains("Multiple Failures (2 failures)"))))) + finishedWithFailure(instanceOf(MultipleAssertionsError.class), + message(msg -> msg.contains("2 assertion errors"))))) .haveExactly(nested ? 2 : 1, event(test("parameterizedTest"), - finishedWithFailure(instanceOf(AssertJMultipleFailuresError.class), - message(msg -> msg.contains("Multiple Failures (1 failure)"))))); + finishedWithFailure(instanceOf(MultipleAssertionsError.class), + message(msg -> msg.contains("1 assertion error"))))); // @format:on } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java index c6b465c281..9880633f8d 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/api/junit/jupiter/SoftAssertionsExtensionAPIIntegrationTest.java @@ -33,7 +33,7 @@ import org.assertj.core.api.DefaultAssertionErrorCollector; import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; -import org.assertj.core.error.AssertJMultipleFailuresError; +import org.assertj.core.error.MultipleAssertionsError; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -116,12 +116,12 @@ void apiTest() { // @format:off .assertThatEvents().haveExactly(1, event(test("multipleFailuresCustom"), - finishedWithFailure(instanceOf(AssertJMultipleFailuresError.class), - message(msg -> msg.contains("Multiple Failures (4 failures)"))))) + finishedWithFailure(instanceOf(MultipleAssertionsError.class), + message(msg -> msg.contains("4 assertion errors"))))) .haveExactly(1, event(test("multipleFailuresBDD"), - finishedWithFailure(instanceOf(AssertJMultipleFailuresError.class), - message(msg -> msg.contains("Multiple Failures (3 failures)"))))); + finishedWithFailure(instanceOf(MultipleAssertionsError.class), + message(msg -> msg.contains("3 assertion errors"))))); // @format:on try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { List collected = APITest.map.get("multipleFailuresCustom").assertionErrorsCollected(); diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/presentation/StandardRepresentation_throwable_format_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/presentation/StandardRepresentation_throwable_format_Test.java index de774693fd..a7b72293e1 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/presentation/StandardRepresentation_throwable_format_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/presentation/StandardRepresentation_throwable_format_Test.java @@ -24,12 +24,24 @@ import org.assertj.core.configuration.Configuration; import org.assertj.core.presentation.Representation; import org.assertj.core.presentation.StandardRepresentation; -import org.assertj.tests.core.testkit.MutatesGlobalConfiguration; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -@MutatesGlobalConfiguration class StandardRepresentation_throwable_format_Test { + private int initialMaxStackTraceElementsDisplayedValue; + + @BeforeEach + public void beforeTest() { + initialMaxStackTraceElementsDisplayedValue = StandardRepresentation.getMaxStackTraceElementsDisplayed(); + } + + @AfterEach + public void afterTest() { + StandardRepresentation.setMaxStackTraceElementsDisplayed(initialMaxStackTraceElementsDisplayedValue); + } + private static final Representation REPRESENTATION = new StandardRepresentation(); // Just to make sure the stack trace is long enough. diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/ClasspathResources.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/ClasspathResources.java index 161b5b89b7..56a3952811 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/ClasspathResources.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/ClasspathResources.java @@ -15,7 +15,7 @@ */ package org.assertj.tests.core.testkit; -import static org.junit.platform.commons.support.ReflectionSupport.findAllResourcesInModule; +import static org.junit.platform.commons.support.ResourceSupport.findAllResourcesInModule; import java.io.File; import java.net.MalformedURLException; @@ -23,7 +23,9 @@ import java.net.URL; import java.nio.file.Path; import java.util.List; -import org.junit.platform.commons.support.Resource; + +import org.junit.platform.commons.io.Resource; +import org.junit.platform.commons.io.ResourceFilter; public class ClasspathResources { @@ -44,8 +46,8 @@ public static URL resourceURL(String resourceName) { } public static URI resourceURI(String resourceName) { - List resources = findAllResourcesInModule(ClasspathResources.class.getModule().getName(), - resource -> resource.getName().equals(resourceName)); + ResourceFilter filter = ResourceFilter.of(resource -> resource.getName().equals(resourceName)); + List resources = findAllResourcesInModule(ClasspathResources.class.getModule().getName(), filter); if (resources.size() != 1) throw new IllegalStateException("Unique resource not found: " + resources); return resources.getFirst().getUri(); } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/MutatesGlobalConfiguration.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/MutatesGlobalConfiguration.java index 6eef8ec14c..f4a40b1e7f 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/MutatesGlobalConfiguration.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/assertj/tests/core/testkit/MutatesGlobalConfiguration.java @@ -17,9 +17,11 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Target; + import org.assertj.core.configuration.ConfigurationProvider; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.parallel.Isolated; @@ -37,7 +39,12 @@ @Target(ElementType.TYPE) public @interface MutatesGlobalConfiguration { - final class AssumptionMutatingExtension implements AfterEachCallback, AfterAllCallback { + final class AssumptionMutatingExtension implements BeforeEachCallback, AfterEachCallback, AfterAllCallback { + + @Override + public void beforeEach(ExtensionContext context) { + resetAll(); + } @Override public void afterEach(ExtensionContext context) { diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/custom/CustomAsserts_filter_stacktrace_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/CustomAsserts_filter_stacktrace_Test.java similarity index 69% rename from assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/custom/CustomAsserts_filter_stacktrace_Test.java rename to assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/CustomAsserts_filter_stacktrace_Test.java index 45a1a02d38..2385170443 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/custom/CustomAsserts_filter_stacktrace_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/CustomAsserts_filter_stacktrace_Test.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.example.custom; +package org.example.test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.tests.core.util.AssertionsUtil.expectAssertionError; import java.util.stream.Stream; + import org.assertj.core.api.AbstractObjectAssert; import org.assertj.core.api.Assertions; import org.assertj.core.api.Condition; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.assertj.core.error.BasicErrorMessageFactory; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.assertj.core.internal.Failures; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -51,61 +51,55 @@ void stacktrace_should_not_include_assertj_elements_nor_elements_coming_from_ass @Test void should_filter_when_custom_assert_fails_with_message() { - try { - new CustomAssert("").fail(); - } catch (AssertionError e) { - assertThat(e.getStackTrace()).areNot(elementOf(CustomAssert.class)); - } + // WHEN + var assertionError = expectAssertionError(() -> new CustomAssert("").fail()); + // THEN + then(assertionError.getStackTrace()).areNot(elementOfCustomAssert()); } @Test void should_filter_when_custom_assert_fails_with_overridden_message() { - try { - new CustomAssert("").overridingErrorMessage("overridden message").fail(); - } catch (AssertionError e) { - assertThat(e.getStackTrace()).areNot(elementOf(CustomAssert.class)); - } + // WHEN + var assertionError = expectAssertionError(() -> new CustomAssert("").overridingErrorMessage("overridden message").fail()); + // THEN + then(assertionError.getStackTrace()).areNot(elementOfCustomAssert()); } @Test void should_filter_when_custom_assert_throws_assertion_error() { - try { - new CustomAssert("").throwAnAssertionError(); - } catch (AssertionError e) { - assertThat(e.getStackTrace()).areNot(elementOf(CustomAssert.class)); - } + // WHEN + var assertionError = expectAssertionError(() -> new CustomAssert("").throwAnAssertionError()); + // THEN + then(assertionError.getStackTrace()).areNot(elementOfCustomAssert()); } @Test void should_filter_when_abstract_custom_assert_fails() { - try { - new CustomAssert("").failInAbstractAssert(); - } catch (AssertionError e) { - assertThat(e.getStackTrace()).areNot(elementOf(CustomAbstractAssert.class)); - } + // WHEN + var assertionError = expectAssertionError(() -> new CustomAssert("").failInAbstractAssert()); + // THEN + then(assertionError.getStackTrace()).areNot(elementOfCustomAssert()); } @Test void should_not_filter_when_global_remove_option_is_disabled() { + boolean initialValue = Failures.instance().isRemoveAssertJRelatedElementsFromStackTrace(); Assertions.setRemoveAssertJRelatedElementsFromStackTrace(false); try { - new CustomAssert("").fail(); - } catch (AssertionError e) { - assertThat(e.getStackTrace()).areAtLeastOne(elementOf(CustomAssert.class)); + // WHEN + var assertionError = expectAssertionError(() -> new CustomAssert("").fail()); + // THEN + then(assertionError.getStackTrace()).areAtLeastOne(elementOfCustomAssert()); + } finally { + Assertions.setRemoveAssertJRelatedElementsFromStackTrace(initialValue); } } - @BeforeEach - @AfterEach - void enableStackTraceFiltering() { - Assertions.setRemoveAssertJRelatedElementsFromStackTrace(true); - } - - private static Condition elementOf(final Class clazz) { - return new Condition("StackTraceElement of " + clazz) { + private static Condition elementOfCustomAssert() { + return new Condition<>("StackTraceElement of " + CustomAssert.class) { @Override public boolean matches(StackTraceElement value) { - return value.getClassName().equals(clazz.getName()); + return value.getClassName().equals(CustomAssert.class.getName()); } }; } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/DefaultAssertionErrorCollector_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/DefaultAssertionErrorCollector_Test.java index 857fcef857..9465d27e79 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/DefaultAssertionErrorCollector_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/DefaultAssertionErrorCollector_Test.java @@ -20,42 +20,22 @@ import static org.assertj.core.extractor.Extractors.byName; import static org.assertj.tests.core.util.AssertionsUtil.expectAssertionError; -import java.util.List; - import org.assertj.core.api.DefaultAssertionErrorCollector; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; // not in an assertj package to be able to check the stack trace as we filter the stack trace element in assertj packages -@DisplayName("DefaultAssertionErrorCollector assertionErrorsCollected") class DefaultAssertionErrorCollector_Test { - private final DefaultAssertionErrorCollector underTest = new DefaultAssertionErrorCollector(); - - @Test - void collected_errors_should_be_decorate_with_line_numbers() { - // GIVEN - AssertionError error1 = expectAssertionError(() -> assertThat("foo").isEqualTo("bar")); - AssertionError error2 = expectAssertionError(() -> assertThat(1).isNegative()); - underTest.collectAssertionError(error1); - underTest.collectAssertionError(error2); - // WHEN - List decoratedErrors = underTest.assertionErrorsCollected(); - // THEN - then(decoratedErrors.get(0)).hasMessageContainingAll("at DefaultAssertionErrorCollector_Test.lambda", - "(DefaultAssertionErrorCollector_Test.java:39)"); - then(decoratedErrors.get(1)).hasMessageContainingAll("at DefaultAssertionErrorCollector_Test.lambda", - "(DefaultAssertionErrorCollector_Test.java:40)"); - } + private final DefaultAssertionErrorCollector defaultAssertionErrorCollector = new DefaultAssertionErrorCollector(); @Test void decorated_AssertionFailedError_should_keep_actual_and_expected_values_when_populated() { // GIVEN var error = expectAssertionError(() -> assertThat("foo").isEqualTo("bar")); - underTest.collectAssertionError(error); + defaultAssertionErrorCollector.collectAssertionError(error); // WHEN - AssertionError decoratedError = underTest.assertionErrorsCollected().get(0); + AssertionError decoratedError = defaultAssertionErrorCollector.assertionErrorsCollected().get(0); // THEN then(decoratedError).isInstanceOf(AssertionFailedError.class); Object actualInOriginalError = byName("actual.value").apply(error); @@ -70,14 +50,13 @@ void decorated_AssertionFailedError_should_keep_actual_and_expected_values_when_ void decorated_AssertionFailedError_should_not_have_null_actual_and_expected_values_when_not_populated() { // GIVEN AssertionError error = new AssertionFailedError("boom"); - underTest.collectAssertionError(error); + defaultAssertionErrorCollector.collectAssertionError(error); // WHEN - AssertionError decoratedError = underTest.assertionErrorsCollected().get(0); + AssertionError decoratedError = defaultAssertionErrorCollector.assertionErrorsCollected().get(0); // THEN then(decoratedError).isInstanceOf(AssertionFailedError.class) - .hasMessageContainingAll(error.getMessage(), - "(DefaultAssertionErrorCollector_Test.java:72)"); - AssertionFailedError decoratedAssertionFailedError = (AssertionFailedError) decoratedError; + .hasMessage(error.getMessage()); + var decoratedAssertionFailedError = (AssertionFailedError) decoratedError; then(decoratedAssertionFailedError.isActualDefined()).isFalse(); then(decoratedAssertionFailedError.isExpectedDefined()).isFalse(); } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Remove_assertJ_stacktrace_elements_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Remove_assertJ_stacktrace_elements_Test.java index a3bc6c0440..092d7ef584 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Remove_assertJ_stacktrace_elements_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Remove_assertJ_stacktrace_elements_Test.java @@ -74,10 +74,6 @@ private static void checkTestClassStackTraceElementsAreConsecutive(AssertionErro static Stream stacktrace_should_not_include_assertj_elements_nor_elements_coming_from_assertj() { return Stream.of(() -> assertThat(0).isEqualTo(1), () -> assertThat(0).satisfies(x -> assertThat(x).isEqualTo(1)), - () -> assertThat(0).satisfies(x -> { - assertThat(0).satisfies(y -> { - assertThat(2).isEqualTo(1); - }); - })); + () -> assertThat(0).satisfies(_ -> assertThat(0).satisfies(_ -> assertThat(2).isEqualTo(1)))); } } diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/custom/SoftAssertionsErrorDescriptionTest.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/SoftAssertionsErrorDescriptionTest.java similarity index 61% rename from assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/custom/SoftAssertionsErrorDescriptionTest.java rename to assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/SoftAssertionsErrorDescriptionTest.java index b1b0f1118a..8d720e29d1 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/custom/SoftAssertionsErrorDescriptionTest.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/SoftAssertionsErrorDescriptionTest.java @@ -13,17 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.example.custom; +package org.example.test; -import static java.lang.String.format; import static org.assertj.core.api.BDDAssertions.then; import static org.assertj.tests.core.util.AssertionsUtil.expectAssertionError; import org.assertj.core.api.SoftAssertions; +import org.assertj.core.presentation.StandardRepresentation; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SoftAssertionsErrorDescriptionTest { + private int initialMaxStackTraceElementsDisplayedValue; + + @BeforeEach + public void beforeTest() { + initialMaxStackTraceElementsDisplayedValue = StandardRepresentation.getMaxStackTraceElementsDisplayed(); + } + + @AfterEach + public void afterTest() { + StandardRepresentation.setMaxStackTraceElementsDisplayed(initialMaxStackTraceElementsDisplayedValue); + } + @Test public void should_display_the_error_cause_and_the_cause_first_stack_trace_elements() { // GIVEN @@ -32,10 +46,9 @@ public void should_display_the_error_cause_and_the_cause_first_stack_trace_eleme // WHEN var error = expectAssertionError(softly::assertAll); // THEN - then(error).hasMessageStartingWith(format("%nMultiple Failures (1 failure)%n" - + "-- failure 1 --" - + "failure%n" - + "at SoftAssertionsErrorDescriptionTest.should_display_the_error_cause_and_the_cause_first_stack_trace_elements(SoftAssertionsErrorDescriptionTest.java:31)")); + then(error).hasMessageContainingAll("cause message: abc", + "cause first 3 stack trace elements:", + "SoftAssertionsErrorDescriptionTest.throwRuntimeException(SoftAssertionsErrorDescriptionTest.java:55)"); } protected static RuntimeException throwRuntimeException() { diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/StandardRepresentation_MultipleAssertionsError_format_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/StandardRepresentation_MultipleAssertionsError_format_Test.java new file mode 100644 index 0000000000..50b0a464fa --- /dev/null +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/StandardRepresentation_MultipleAssertionsError_format_Test.java @@ -0,0 +1,129 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example.test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.tests.core.util.AssertionsUtil.expectAssertionError; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.assertj.core.description.TextDescription; +import org.assertj.core.error.MultipleAssertionsError; +import org.assertj.core.presentation.StandardRepresentation; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * this is not in a org.assertj package to avoid assertj packages in the stack trace that is handled specifically and + * does not reflect real users code. + */ +class StandardRepresentation_MultipleAssertionsError_format_Test { + + private static final StandardRepresentation REPRESENTATION = new StandardRepresentation(); + private static final List throwables = List.of(expectAssertionError(() -> assertThat("foo").startsWith("bar")), + expectAssertionError(() -> assertThat("baz").isEqualTo("bar"))); + private int initialMaxStackTraceElementsDisplayedValue; + + @BeforeEach + public void beforeTest() { + initialMaxStackTraceElementsDisplayedValue = StandardRepresentation.getMaxStackTraceElementsDisplayed(); + } + + @AfterEach + public void afterTest() { + StandardRepresentation.setMaxStackTraceElementsDisplayed(initialMaxStackTraceElementsDisplayedValue); + } + + @Test + void should_not_display_stacktrace_if_maxStackTraceElementsDisplayed_is_zero() { + // GIVEN + StandardRepresentation.setMaxStackTraceElementsDisplayed(0); + var multipleAssertionsError = new MultipleAssertionsError(null, null, throwables); + // WHEN + String toString = REPRESENTATION.toStringOf(multipleAssertionsError); + // THEN + //@format:off + then(toString).isEqualTo(""" + 2 assertion errors: + + -- error 1 -- + Expecting actual: + "foo" + to start with: + "bar" + + -- error 2 -- + expected: "bar" + but was: "baz\"""".replaceAll("\\n", System.lineSeparator())); + //@format:on + } + + @Test + void should_display_the_configured_number_of_stacktrace_elements() { + // GIVEN + StandardRepresentation.setMaxStackTraceElementsDisplayed(2); + var multipleAssertionsError = new MultipleAssertionsError(null, null, throwables); + // WHEN + String toString = REPRESENTATION.toStringOf(multipleAssertionsError); + // THEN + then(toString).containsSubsequence("2 assertion errors:", + "-- error 1 --", + "first 2 stack trace elements:", + "-- error 2 --", + "first 2 stack trace elements:"); + } + + @Test + void should_display_the_full_stacktrace() { + // GIVEN + StandardRepresentation.setMaxStackTraceElementsDisplayed(100); + var multipleAssertionsError = new MultipleAssertionsError(null, null, throwables); + // WHEN + String toString = REPRESENTATION.toStringOf(multipleAssertionsError); + // THEN + then(toString).containsSubsequence("2 assertion errors:", + "-- error 1 --", + "first 100 stack trace elements:") + .doesNotContain("remaining lines not displayed"); + } + + @Test + void should_display_toString_when_null_stack() { + // GIVEN + Throwable throwable = mock(); + when(throwable.toString()).thenReturn("throwable string"); + // WHEN + String actual = REPRESENTATION.toStringOf(throwable); + // THEN + then(actual).isEqualTo("throwable string"); + } + + @Test + void should_honor_description() { + // GIVEN + String description = "assertions description"; + var multipleAssertionsError = new MultipleAssertionsError(new TextDescription(description), null, throwables); + // WHEN + String errorRepresentation = REPRESENTATION.toStringOf(multipleAssertionsError); + // THEN + then(errorRepresentation).containsSubsequence("[assertions description]", + "2 assertion errors:"); + } +} diff --git a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Throwables_removeAssertJElementFromStackTrace_Test.java b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Throwables_removeAssertJElementFromStackTrace_Test.java index 6ea2bde8eb..8a891a02c1 100644 --- a/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Throwables_removeAssertJElementFromStackTrace_Test.java +++ b/assertj-tests/assertj-integration-tests/assertj-core-tests/src/test/java/org/example/test/Throwables_removeAssertJElementFromStackTrace_Test.java @@ -15,12 +15,12 @@ */ package org.example.test; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.util.Throwables.removeAssertJRelatedElementsFromStackTrace; import static org.assertj.tests.core.testkit.StackTraceUtils.checkNoAssertjStackTraceElementIn; import static org.assertj.tests.core.testkit.StackTraceUtils.hasAssertJStackTraceElement; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; /** @@ -32,7 +32,7 @@ class Throwables_removeAssertJElementFromStackTrace_Test { void should_add_stack_trace_of_current_thread() { // GIVEN Throwable throwable = catchThrowable(this::throwAssertJThrowable); - Assertions.assertThat(hasAssertJStackTraceElement(throwable)).isTrue(); + assertThat(hasAssertJStackTraceElement(throwable)).isTrue(); // WHEN removeAssertJRelatedElementsFromStackTrace(throwable); // THEN diff --git a/assertj-tests/assertj-integration-tests/assertj-scripts/src/test/java/org/assertj/scripts/ClasspathResources.java b/assertj-tests/assertj-integration-tests/assertj-scripts/src/test/java/org/assertj/scripts/ClasspathResources.java index ab9cb1bda2..91e0503c80 100644 --- a/assertj-tests/assertj-integration-tests/assertj-scripts/src/test/java/org/assertj/scripts/ClasspathResources.java +++ b/assertj-tests/assertj-integration-tests/assertj-scripts/src/test/java/org/assertj/scripts/ClasspathResources.java @@ -15,17 +15,19 @@ */ package org.assertj.scripts; -import static org.junit.platform.commons.support.ReflectionSupport.findAllResourcesInPackage; +import static org.junit.platform.commons.support.ResourceSupport.findAllResourcesInPackage; import java.nio.file.Path; import java.util.List; -import org.junit.platform.commons.support.Resource; +import org.junit.platform.commons.io.Resource; +import org.junit.platform.commons.io.ResourceFilter; class ClasspathResources { static Path resourcePath(String resourceName) { - List resources = findAllResourcesInPackage("", resource -> resource.getName().equals(resourceName)); + ResourceFilter filter = ResourceFilter.of(resource -> resource.getName().equals(resourceName)); + List resources = findAllResourcesInPackage("", filter); if (resources.size() != 1) throw new IllegalStateException("Unique resource not found: " + resources); return Path.of(resources.getFirst().getUri()); } diff --git a/assertj-tests/assertj-integration-tests/pom.xml b/assertj-tests/assertj-integration-tests/pom.xml index 4c835d7b9c..9f0aa3c809 100644 --- a/assertj-tests/assertj-integration-tests/pom.xml +++ b/assertj-tests/assertj-integration-tests/pom.xml @@ -14,6 +14,7 @@ AssertJ Integration Tests + assertj-core-osgi assertj-core-spring-boot assertj-core-tests assertj-guava-tests @@ -30,15 +31,6 @@ assertj-core-kotlin - - java-24-incompatible - - (,24) - - - assertj-core-osgi - - unix diff --git a/pom.xml b/pom.xml index 5e2f418d63..d940907730 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,7 @@ 3.2.8 3.1.4 3.5.0 - 3.7.0 + 3.12.0 3.3.1 3.4.0 3.21.0 @@ -368,6 +368,20 @@ limitations under the License. + + + Central Portal Snapshots + central-portal-snapshots + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + + + + publish