断言是测试用例的一部分,也是测试工程师开发测试用例的核心。断言通常集成在单元测试和集成测试中,断言分为硬断言和软断言。
硬断言是我们狭义上听到的普通断言:当用例运行后得到的[实际]结果与预期结果不匹配时,测试框架将停止测试执行并抛出断言错误。如下面的案例,测试执行在第一个失败时停止,即使测试中有更多的断言也不会继续执行。
- import org.assertj.core.api.SoftAssertions;
- import org.junit.jupiter.api.Test;
-
- class HardAssertionTest {
-
- @Test
- void hardAssertion() {
- var person = Person.builder().name("John").phoneNumber(null).age(16).build();
-
- assertThat(person.getName()).isNotBlank();
- assertThat(person.getPhoneNumber()).isNotNull();
- assertThat(person.getAge()).isGreaterThan(18);
- }
- }
第10行断言失败,则第11行和第12行的断言将不会被执行。
- java.lang.AssertionError:
- Expecting actual not to be null
通常来说,硬断言在断言失败情况下终止测试执行是无可厚非的。但是在某些情况下,我们想知道被断言的“对象”里哪些属性是不符合预期的,这样使用软断言将解决这个问题。
软断言:当一个测试用例中存在多个断言,当有一个断言失败时,会执行后续的断言。
支持软断言的工具通常使用下面的伪代码实现。
- SoftAssertion softAssertion = new SoftAssertion()
-
- softAssertion.assertSomething...
- softAssertion.assertAnotherThing...
- softAssertion.assertTheLastThing...
-
- softAssertion.assertThenAll();
什么情况下使用软断言?
当测试用例有多个断言时候,应该使用软断言。因为知道所有断言是否与预期结果一致,可以减少多次运行测试以了解哪些不通过。
当测试用例有不止一条断言时,软断言就要到位了。
TestNG有一个SoftAssert类,它的功能与前面介绍的伪代码相同:它对断言进行分组,并在我们调用特定方法时立即进行验证。
来看一个软断言的案例:
- public class SoftAssertTestNGTest {
-
- @Test
- public void testNGSoftAssertion() {
- var person = Person.builder().name("John").phoneNumber(null).age(16).build();
- SoftAssert softAssert = new SoftAssert();
-
- softAssert.assertEquals(person.getName(), "John");
- softAssert.assertNotNull(person.getPhoneNumber(), "Phone number cannot be null");
- softAssert.assertEquals(person.getAge(), 25, "Age should be equal");
-
- softAssert.assertAll();
- }
- }
在例子中,我们断言:phoneNumber不能为空,年龄必须等于25。
我们可以看到显示一个断言失败,而TestNG没有停止测试执行,而是运行所有的断言,显示所有的失败。
- java.lang.AssertionError: The following asserts failed:
- Phone number cannot be null
- Expected :25
- Actual :16
- <Click to see difference>
JUnit 5使用assertAll()方法作为软断言方法。它不需要使用外部特定类,它已经是断言类的一部分。我们需要做的就是静态方式导入它。
例子:
- import org.junit.jupiter.api.Test;
-
- import static org.junit.jupiter.api.Assertions.assertAll;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertNotNull;
-
- class SoftAssertionJunit5Test {
- @Test
- void softAssertionUsingJUnit5() {
- var person = Person.builder().name("John").phoneNumber(null).age(16).build();
-
- assertAll("person",
- () -> assertNotNull(person.getName(), "Name must not be null"),
- () -> assertNotNull(person.getPhoneNumber(), "Phone number should not be null"),
- () -> assertEquals(18., person.getAge(), "Age must be 18")
- );
- }
- }
执行结果如下:
- org.opentest4j.AssertionFailedError: Phone number should not be null ==> expected: not <null>
- at org.example.SoftAssertionJunit5Test.lambda$softAssertionUsingJUnit5$1(SoftAssertionJunit5Test.java:17)
-
- org.opentest4j.AssertionFailedError: Age must be 18 ==>
- Expected :18.0
- Actual :16.0
- at org.example.SoftAssertionJunit5Test.lambda$softAssertionUsingJUnit5$2(SoftAssertionJunit5Test.java:18)
-
- org.opentest4j.MultipleFailuresError: person (2 failures)
- org.opentest4j.AssertionFailedError: Phone number should not be null ==> expected: not <null>
- org.opentest4j.AssertionFailedError: Age must be 18 ==> expected: <18.0> but was: <16.0>
AssertJ断言库提供了软断言的不同方法,并可以创建自己的断言:
我们可以使用所有这些不同的方法来应用它,并在AssertJ页面上查看所有示例。这里可以看到assertsoft静态方法的使用案例。
- import org.assertj.core.api.SoftAssertions;
- import org.junit.jupiter.api.Test;
-
- class SoftAssertionTest {
-
- @Test
- void softAssertionUsingAssertJ() {
- var person = Person.builder().name("John").phoneNumber(null).age(16).build();
-
- SoftAssertions.assertSoftly(softly -> {
- softly.assertThat(person.getName()).isNotBlank();
- softly.assertThat(person.getPhoneNumber()).isNotNull();
- softly.assertThat(person.getAge()).isGreaterThan(18);
- });
- }
- }
异常输出如下:
- java.lang.AssertionError:
- Expecting actual not to be null
- at SoftAssertionTest.lambda$assertJSoftAssertion$0(SoftAssertionTest.java:16)
-
- java.lang.AssertionError:
- Expecting actual:
- 16
- to be greater than:
- 18
- at SoftAssertionTest.lambda$assertJSoftAssertion$0(SoftAssertionTest.java:17)
-
- org.assertj.core.error.AssertJMultipleFailuresError:
- Multiple Failures (2 failures)
- -- failure 1 --
- Expecting actual not to be null
- at SoftAssertionTest.lambda$assertJSoftAssertion$0(SoftAssertionTest.java:16)
- -- failure 2 --
- Expecting actual:
- 16
- to be greater than:
- 18
- at SoftAssertionTest.lambda$assertJSoftAssertion$0(SoftAssertionTest.java:17)