精华内容
下载资源
问答
  • junit单元测试断言 简而言之,本章涵盖了各种单元测试声明技术。 它详细说明了内置机制, Hamcrest匹配器和AssertJ断言的优缺点 。 正在进行的示例扩大了主题,并说明了如何创建和使用自定义匹配器/断言。 单元...

    junit单元测试断言

    简而言之,本章涵盖了各种单元测试声明技术。 它详细说明了内置机制, Hamcrest匹配器和AssertJ断言的优缺点 。 正在进行的示例扩大了主题,并说明了如何创建和使用自定义匹配器/断言。

    单元测试断言

    信任但要验证
    罗纳德·里根(Ronald Reagan)

    后期测试结构解释了为什么单元测试通常分阶段进行。 它澄清说, 真正的测试结果验证在第三阶段进行。 但是到目前为止,我们只看到了一些简单的示例,主要使用了JUnit的内置机制。

    Hello World所示,验证基于错误类型AssertionError 。 这是编写所谓的自检测试的基础。 单元测试断言将谓词评估为truefalse 。 如果为false ,则抛出AssertionError 。 JUnit运行时捕获此错误并将测试报告为失败。

    以下各节将介绍三种较流行的单元测试断言变体。

    断言

    JUnit的内置断言机制由类org.junit.Assert 。 它提供了两种静态方法来简化测试验证。 以下代码片段概述了可用方法模式的用法:

    fail();
    fail( "Houston, We've Got a Problem." );
    
    assertNull( actual );
    assertNull( "Identifier must not be null.",
                actual );
    
    assertTrue( counter.hasNext() );
    assertTrue( "Counter should have a successor.",
                counter.hasNext() );
    
    assertEquals( LOWER_BOUND, actual );
    assertEquals( "Number should be lower bound value.", 
                  LOWER_BOUND,
                  actual );
    1. Assert#fail()无条件地引发断言错误。 这对于标记不完整的测试或确保引发了预期的异常很有帮助(另请参见“ 测试结构”中的“预期异常”部分)。
    2. Assert#assertXXX(Object)用于验证变量的初始化状态。 为此,存在两个称为assertNull(Object)assertNotNull(Object)
    3. Assert#assertXXX(boolean)方法测试boolean参数传递的预期条件。 调用assertTrue(boolean)期望条件为true ,而assertFalse(boolean)期望相反。
    4. Assert#assertXXX(Object,Object)Assert#assertXXX(value,value)方法用于对值,对象和数组进行比较验证。 尽管结果没有区别,但通常的做法是将期望值作为第一个参数,将实际值作为第二个参数。

    所有这些类型的方法都提供带有String参数的重载版本。 如果发生故障,此参数将合并到断言错误消息中。 许多人认为这有助于更清楚地指定失败原因。 其他人则认为此类消息混乱,使测试更难阅读。

    乍一看,这种单元测试断言似乎很直观。 这就是为什么我在前面的章节中使用它进行入门的原因。 此外,它仍然非常流行,并且工具很好地支持故障报告。 但是,在需要更复杂的谓词的断言的表达性方面也受到一定限制。

    Hamcrest

    Hamcrest是一个旨在提供用于创建灵活的意图表达的API的库。 该实用程序提供了称为Matcher的可嵌套谓词。 这些允许以某种方式编写复杂的验证条件,许多开发人员认为比布尔运算符更易于阅读。

    MatcherAssert类支持单元测试断言。 为此,它提供了静态的assertThat(T, Matcher )方法。 传递的第一个参数是要验证的值或对象。 第二个谓词用于评估第一个谓词。

    assertThat( actual, equalTo( IN_RANGE_NUMBER ) );

    如您所见,匹配器方法模仿自然语言的流程以提高可读性。 以下代码片段更加清楚了此意图。 这使用is(Matcher )方法来修饰实际的表达式。

    assertThat( actual, is( equalTo( IN_RANGE_NUMBER ) ) );

    MatcherAssert.assertThat(...)存在另外两个签名。 首先,有一个采用布尔参数而不是Matcher参数的变量。 它的行为与Assert.assertTrue(boolean)

    第二个变体将一个附加的String传递给该方法。 这可以用来提高故障消息的表达能力:

    assertThat( "Actual number must not be equals to lower bound value.", 
                 actual, 
                 is( not( equalTo( LOWER_BOUND ) ) ) );

    在失败的情况下,给定验证的错误消息如下所示:

    hamcrest-failure

    Hamcrest带有一组有用的匹配器。 图书馆在线文档的“常见匹配项”部分中列出了最重要的部分。 但是对于特定域的问题,如果有合适的匹配器,通常可以提高单元测试断言的可读性。

    因此,该库允许编写自定义匹配器。

    让我们返回教程的示例来讨论该主题。 首先,我们对该场景进行调整以使其更合理。 假设NumberRangeCounter.next()返回的是RangeNumber类型,而不是简单的int值:

    public class RangeNumber {
      
      private final String rangeIdentifier;
      private final int value;
    
      RangeNumber( String rangeIdentifier, int value  ) {
        this.rangeIdentifier = rangeIdentifier;
        this.value = value;
      }
      
      public String getRangeIdentifier() {
        return rangeIdentifier;
      }
      
      public int getValue() {
        return value;
      }
    }

    我们可以使用自定义匹配器来检查NumberRangeCounter#next()的返回值是否在计数器的定义数字范围内:

    RangeNumber actual = counter.next();
    
    assertThat( actual, is( inRangeOf( LOWER_BOUND, RANGE ) ) );

    适当的自定义匹配器可以扩展抽象类TypeSafeMatcher<T> 。 该基类处理null检查和类型安全。 可能的实现如下所示。 请注意如何添加工厂方法inRangeOf(int,int)以便于使用:

    public class InRangeMatcher extends TypeSafeMatcher<RangeNumber> {
    
      private final int lowerBound;
      private final int upperBound;
    
      InRangeMatcher( int lowerBound, int range ) {
        this.lowerBound = lowerBound;
        this.upperBound = lowerBound + range;
      }
      
      @Override
      public void describeTo( Description description ) {
        String text = format( "between <%s> and <%s>.", lowerBound, upperBound );
        description.appendText( text );
      }
      
      @Override
      protected void describeMismatchSafely(
        RangeNumber item, Description description )
      {
        description.appendText( "was " ).appendValue( item.getValue() );
      }
    
    
      @Override
      protected boolean matchesSafely( RangeNumber toMatch ) {
        return    lowerBound <= toMatch.getValue() 
               && upperBound > toMatch.getValue();
      }
      
      public static Matcher<RangeNumber> inRangeOf( int lowerBound, int range ) {
        return new InRangeMatcher( lowerBound, range );
      }
    }

    对于给定的示例,工作量可能会有些夸大。 但它显示了如何使用自定义匹配器消除先前帖子中有点神奇的IN_RANGE_NUMBER常量。 除了新类型外,还强制声明语句的编译时类型安全。 这意味着例如String参数将不被接受进行验证。

    下图显示了使用我们的自定义匹配器时测试结果失败的样子:

    hamcrest-custom-failure

    很容易看出describeTodescribeMismatchSafely的实现以哪种方式影响故障消息。 它表示期望值应该在指定的下限和(计算的)上限1之间 ,并跟在实际值之后。

    有点不幸的是,JUnit扩展了其Assert类的API,以提供一组assertThat(…)方法。 这些方法实际上复制了MatcherAssert提供的API。 实际上,这些方法的实现委托给这种类型的相应方法。

    尽管这可能只是个小问题,但我认为值得一提。 由于这种方法,JUnit与Hamcrest库牢固地联系在一起。 这种依赖性有时会导致问题。 特别是与其他库一起使用时,通过合并自己的hamcrest版本的副本,情况甚至更糟……

    Hamcrest的单元测试主张并非没有竞争。 虽然关于每次测试一个确定每个测试 一个概念的讨论超出了本文的讨论范围,但后一种观点的支持者可能认为该库的验证声明过于嘈杂。 尤其是当一个概念需要多个断言时。

    这就是为什么我必须在本章中添加另一部分!

    断言

    在“ 测试跑步者”中,示例片段之一使用了两个assertXXX语句。 这些验证期望的异常是IllegalArgumentException的实例并提供特定的错误消息。 该段看起来像这样:

    Throwable actual = ...
    
    assertTrue( actual instanceof IllegalArgumentException );
    assertEquals( EXPECTED_ERROR_MESSAGE, actual.getMessage() );

    上一节教我们如何使用Hamcrest改进代码。 但是,如果您碰巧是该库的新手,您可能会想知道要使用哪个表达式。 或打字可能会感到不舒服。 无论如何,多个assertThat语句会加在一起。

    AssertJ库通过为Java提供流畅的断言来努力改善这一点。 流畅的接口 API的目的是提供一种易于阅读的,富有表现力的编程风格,从而减少胶合代码并简化键入。

    那么如何使用这种方法来重构上面的代码?

    import static org.assertj.core.api.Assertions.assertThat;

    与其他方法类似,AssertJ提供了一个实用程序类,该类提供了一组静态assertThat方法。 但是这些方法针对给定的参数类型返回特定的断言实现。 这就是所谓的语句链接的起点。

    Throwable actual = ...
    
    assertThat( actual )
      .isInstanceOf( IllegalArgumentException.class )
      .hasMessage( EXPECTED_ERROR_MESSAGE );

    旁观者认为可读性在某种程度上得到了扩展,但无论如何都可以用更紧凑的样式来写断言。 了解如何流畅地添加与被测特定概念相关的各种验证方面。 这种编程方法支持有效的类型输入,因为IDE的内容辅助可以提供给定值类型的可用谓词列表。

    因此,您想向后世提供表现力的失败消息吗? 一种可能是使用describedAs作为链中的第一个链接来注释整个块:

    Throwable actual = ...
    
    assertThat( actual )
      .describedAs( "Expected exception does not match specification." )
      .hasMessage( EXPECTED_ERROR_MESSAGE )
      .isInstanceOf( NullPointerException.class );

    该代码段期望使用NPE,但假设在运行时抛出了IAE。 然后失败的测试运行将提供如下消息:

    断言失败

    也许您希望根据给定的失败原因使您的消息更加细微。 在这种情况下,您可以每个验证规范之前添加一条describedAs语句:

    Throwable actual = ...
    
    assertThat( actual )
      .describedAs( "Message does not match specification." )
      .hasMessage( EXPECTED_ERROR_MESSAGE )
      .describedAs( "Exception type does not match specification." )
      .isInstanceOf( NullPointerException.class );

    还有更多的AssertJ功能可供探索。 但是,要使该帖子保持在范围内,请参阅实用程序的在线文档以获取更多信息。 但是,在结束之前,让我们再次看一下范围内验证示例。 这可以通过自定义断言来解决:

    public class RangeCounterAssertion
      extends AbstractAssert<RangeCounterAssertion, RangeCounter>
    {
    
      private static final String ERR_IN_RANGE_OF 
        = "Expected value to be between <%s> and <%s>, but was <%s>";
      private static final String ERR_RANGE_ID 
        = "Expected range identifier to be <%s>, but was <%s>";
      
      public static RangeCounterAssertion assertThat( RangeCounter actual ) {
        return new RangeCounterAssertion( actual );
      }
      
      public InRangeAssertion hasRangeIdentifier( String expected ) {
        isNotNull();
        if( !actual.getRangeIdentifier().equals( expected ) ) {
          failWithMessage( ERR_RANGE_ID, expected, actual.getRangeIdentifier()  );
        }
        return this;
      }
      
      public RangeCounterAssertion isInRangeOf( int lowerBound, int range ) {
        isNotNull();
        int upperBound = lowerBound + range;
        if( !isInInterval( lowerBound, upperBound ) ) {
          int actualValue = actual.getValue();
          failWithMessage( ERR_IN_RANGE_OF, lowerBound, upperBound, actualValue );
        }
        return this;
      }
    
      private boolean isInInterval( int lowerBound, int upperBound ) {
        return actual.getValue() >= lowerBound 
            && actual.getValue() < upperBound;
      }
    
      private RangeCounterAssertion( Integer actual ) {
        super( actual, RangeCounterAssertion.class );
      }
    }

    自定义断言是扩展AbstractAssert常见做法。 第一个通用参数是断言的类型本身。 流利的链接样式需要它。 第二种是断言所基于的类型。

    该实现提供了两种附加的验证方法,可以按照以下示例进行链接。 因此,这些方法将返回断言实例本身。 请注意, isNotNull()的调用如何确保我们要声明的实际RangeNumber不为null

    定制断言由其工厂方法assertThat(RangeNumber) 。 由于它继承了可用的基本检查,因此断言可以开箱即用地验证非常复杂的规范。

    RangeNumber first = ...
    RangeNumber second = ...
    
    assertThat( first )
      .isInRangeOf( LOWER_BOUND, RANGE )
      .hasRangeIdentifier( EXPECTED_RANGE_ID )
      .isNotSameAs( second );

    为了完整RangNumberAssertion ,以下是RangNumberAssertion的实际运行方式:

    断言自定义失败

    不幸的是,不可能在同一测试用例中使用两种不同的断言类型和静态导入。 当然,假定这些类型遵循assertThat(...)命名约定。 为了避免这种情况,文档建议扩展实用程序类Assertions

    这样的扩展可用于提供静态的assertThat方法,作为所有项目自定义断言的入口。 通过在整个项目中使用此自定义实用程序类,不会发生导入冲突。 在为所有断言提供单一入口点的部分中,可以找到详细的描述在线文档中有关定制断言的 yours + AssertJ

    流利的API的另一个问题是单行链接的语句可能更难调试。 这是因为调试器可能无法在链中设置断点。 此外,可能不清楚哪个方法调用已引起异常。

    但是,正如Wikipedia所说的那样,可以通过将语句分成多行来克服这些问题,如上面的示例所示。 这样,用户可以在链中设置断点,轻松地逐行浏览代码。

    结论

    简而言之,JUnit的这一章介绍了不同的单元测试断言方法,例如该工具的内置机制,Hamcrest匹配器和AssertJ断言。 它概述了一些优缺点,并通过本教程的持续示例对主题进行了扩展。 此外,还展示了如何创建和使用自定义匹配器和断言。

    尽管基于Assert的机制肯定是过时的且不太面向对象,但它仍然具有它的提倡者。 Hamcrest匹配器提供断言和谓词定义的清晰分隔,而AssertJ断言以紧凑且易于使用的编程样式进行评分。 所以现在您选择太多了……

    请注意,这将是本教程有关JUnit测试要点的最后一章。 这并不意味着没有更多要说的了。 恰恰相反! 但这将超出此迷你系列量身定制的范围。 而且您知道他们在说什么: 总是让他们想要更多…

    1. 嗯,我想知道区间边界是否会比下限和范围更直观...

    翻译自: https://www.javacodegeeks.com/2014/09/junit-in-a-nutshell-unit-test-assertion.html

    junit单元测试断言

    展开全文
  • 单元测试 断言值为nullby Edd Yerburgh 埃德·耶堡(Edd Yerburgh) 如何通过使用值断言编写更强大的单元测试 (How to write more powerful unit tests by using value assertions) Unit tests are awesome. Writing...

    单元测试 断言值为null

    by Edd Yerburgh

    埃德·耶堡(Edd Yerburgh)

    如何通过使用值断言编写更强大的单元测试 (How to write more powerful unit tests by using value assertions)

    Unit tests are awesome. Writing unit tests reduces bugs by 40–80%.

    单元测试很棒。 编写单元测试可以将错误减少40–80%

    But you need to do them right. Poorly written unit tests can suffocate a codebase, and cause more problems than they solve.

    但是您需要正确地做它们。 编写不佳的单元测试可能会使代码库窒息,并导致更多的问题而不是解决的。

    One way to improve your unit tests is to use value assertions.

    改善单元测试的一种方法是使用值声明

    In this article we’ll look at what value assertions are, and how to use them to improve your tests.

    在本文中,我们将研究什么是价值断言,以及如何使用它们来改进您的测试。

    了解断言 (Understanding assertions)

    Assertions are functions that check to make sure the code behaved as we expected.

    断言是检查以确保代码行为符合我们预期的函数。

    Different languages have different conventions. In JavaScript, it’s common to follow the expect pattern. This is where you expect a condition to match a value.

    不同的语言有不同的约定。 在JavaScript中,通常遵循expect模式。 这是您expect条件与值匹配的地方。

    We combine the expect function with another function called a matcher.

    我们将expect函数与另一个称为matcher的函数结合在一起。

    In the example below, we expect the result of sum(1,1) to equal 2. The toBe matcher checks that the expect value equals 2.

    在下面的示例中,我们expect sum(1,1)的结果等于2toBe匹配器检查期望值等于2

    expect(sum(1,1)).toBe(2)

    If the result of sum(1,1) equals 2, the function won’t do anything and the test will pass. If sum(1,1) doesn’t equal 2, the function throws an assertion error and the test fails.

    如果sum(1,1)的结果等于2 ,则该函数将不执行任何操作,并且测试将通过。 如果sum(1,1)不等于2 ,则该函数将引发断言错误 ,并且测试将失败。

    调试断言错误 (Debugging assertion errors)

    In test frameworks, assertion errors are formatted to make the message easier to read. Assertion errors let you figure out quickly what went wrong in the test.

    在测试框架中,对断言错误进行格式化以使消息更易于阅读。 断言错误使您可以快速找出测试中出了什么问题。

    You can see a failing Jest assertion error below:

    您可以在下面看到失败的Jest断言错误:

    For some reason, sum(1,1) returned 3.

    由于某些原因, sum(1,1)返回3

    If we check the code, we’ll find someone accidentally added b twice:

    如果我们检查代码,就会发现有人不小心将b加了两次:

    function sum(a,b) {  return a + b + b}

    We can fix the error quickly and get the sum function working again. The assertion error helped us figure out what went wrong and where.

    我们可以快速修复错误,并使sum函数再次运行。 断言错误有助于我们找出问题所在和出处。

    什么是价值主张? (What’s a value assertion?)

    A value assertion is an assertion that compares two values.

    值断言是将两个值进行比较的断言

    We just wrote a value assertion:

    我们只是写了一个价值主张:

    expect(sum(1,1)).toBe(2)

    And it generated the assertion error:

    它产生了断言错误:

    Expected value to be (using ===): 2 Received: 3

    还有哪些其他主张? (What other assertions are there?)

    Another common assertion is a boolean assertion.

    另一个常见的断言是布尔断言

    A boolean assertion is an assertion that compares two booleans.

    布尔断言是将两个布尔值进行比较的断言。

    expect(add(1,1) === 2).toBe(true)

    This generates a boolean assertion error:

    这会产生布尔断言错误:

    Expected value to be (using ===): true Received: false

    调试值断言 (Debugging a value assertion)

    Value assertions throw descriptive assertion errors.

    值断言引发描述性断言错误。

    When a test fails with a value assertion, you can see why the test is failing. This gives us a clue to what is happening in the code:

    当测试失败并带有值断言时,您可以查看测试失败的原因。 这为我们提供了代码中发生情况的线索:

    warning: expected 'somevalue' to equal 'some value'

    You know what to look for in the code when you see an error like this. Oh, it looks like someone deleted a space by accident.

    当您看到这样的错误时,您知道在代码中查找什么。 哦,好像有人不小心删除了一个空格。

    Value assertions improve the debuggability (yes that’s a word) of unit tests. From reading the assertion error, you can see what went wrong in the test.

    值断言提高了单元测试的可调试性(是的)。 通过读取断言错误,您可以查看测试中出了什么问题。

    Let’s look at an assertion error from a boolean assertion:

    让我们看一下来自布尔断言的断言错误:

    What’s gone wrong?

    怎么了?

    It takes longer to debug a test with a boolean assertion, because you don’t know what value was returned by the tested code.

    使用布尔断言调试测试需要更长的时间,因为您不知道被测试的代码返回了什么值。

    This makes boolean assertion errors pretty useless in unit tests.

    这使得布尔断言错误在单元测试中几乎没有用。

    撰写价值主张 (Writing value assertions)

    So we want to write value assertions.

    因此,我们要编写值断言。

    Most JavaScript testing libraries provide functions to write value assertions.

    大多数JavaScript测试库都提供用于编写​​值断言的函数。

    Jest contains tons of useful matchers to create value assertions:

    笑话包含大量有用的匹配器,可以创建价值主张:

    .toBeGreaterThan(number).toContain(item).toHaveBeenCalled().toHaveProperty(keyPath, value)

    呼吁采取行动 (Call to action)

    Now you understand the power of value assertions, your tests will improve.

    现在您了解了价值主张的力量,您的测试将得到改善。

    Get out there and write some debuggable unit tests!

    出去写一些可调试的单元测试!

    If you enjoyed this article, please give me some claps so more people see it. Thanks!

    如果您喜欢这篇文章,请给我一些鼓掌,以便更多的人看到。 谢谢!

    翻译自: https://www.freecodecamp.org/news/how-to-write-powerful-unit-tests-using-value-assertions-3de5146c0088/

    单元测试 断言值为null

    展开全文
  • JUnit 单元测试断言推荐 AssertJ.pdf
  • 简而言之,本章涵盖了各种单元测试断言技术。 它详细说明了内置机制, Hamcrest匹配器和AssertJ断言的优缺点 。 正在进行的示例扩大了该主题,并说明了如何创建和使用自定义匹配器/断言。 单元测试断言 信任但要...

    简而言之,本章涵盖了各种单元测试断言技术。 它详细说明了内置机制, Hamcrest匹配器和AssertJ断言的优缺点 。 正在进行的示例扩大了该主题,并说明了如何创建和使用自定义匹配器/断言。

    单元测试断言

    信任但要验证
    罗纳德·里根(Ronald Reagan)

    岗位测试结构解释了为什么单元测试通常分阶段进行。 它澄清说, 真正的测试结果验证在第三阶段进行。 但是到目前为止,我们只看到了一些简单的示例,主要使用了JUnit的内置机制。

    Hello World所示,验证基于错误类型AssertionError 。 这是编写所谓的自检测试的基础。 单元测试断言将谓词评估为truefalse 。 如果为false ,则抛出AssertionError 。 JUnit运行时捕获此错误并将测试报告为失败。

    以下各节将介绍三种较流行的单元测试断言变体。

    断言

    JUnit的内置断言机制由类org.junit.Assert 。 它提供了两种静态方法来简化测试验证。 以下代码片段概述了可用方法模式的用法:

    fail();
    fail( "Houston, We've Got a Problem." );
    
    assertNull( actual );
    assertNull( "Identifier must not be null.",
                actual );
    
    assertTrue( counter.hasNext() );
    assertTrue( "Counter should have a successor.",
                counter.hasNext() );
    
    assertEquals( LOWER_BOUND, actual );
    assertEquals( "Number should be lower bound value.", 
                  LOWER_BOUND,
                  actual );
    1. Assert#fail()无条件地引发断言错误。 这对于标记不完整的测试或确保引发了预期的异常可能很有帮助(另请参见“ 测试结构”中的“预期异常”部分)。
    2. Assert#assertXXX(Object)用于验证变量的初始化状态。 为此,存在两个称为assertNull(Object)assertNotNull(Object)
    3. Assert#assertXXX(boolean)方法测试boolean参数传递的预期条件。 调用assertTrue(boolean)期望条件为true ,而assertFalse(boolean)期望相反。
    4. Assert#assertXXX(Object,Object)Assert#assertXXX(value,value)方法用于对值,对象和数组进行比较验证。 尽管结果没有区别,但通常的做法是将期望值作为第一个参数,将实际值作为第二个参数。

    所有这些类型的方法都提供带有String参数的重载版本。 如果发生故障,此参数将合并到断言错误消息中。 许多人认为这有助于更清楚地指定失败原因。 其他人则认为这样的消息混乱,使测试更难以阅读。

    乍一看,这种单元测试断言似乎很直观。 这就是为什么我在前面的章节中使用它进行入门的原因。 此外,它仍然非常流行,并且工具很好地支持故障报告。 但是,在需要更复杂谓词的断言的表达性方面也受到一定限制。

    Hamcrest

    Hamcrest是一个旨在提供用于创建灵活的意图表达的API的库。 该实用程序提供了称为Matcher的可嵌套谓词。 这些允许以某种方式编写复杂的验证条件,许多开发人员认为比布尔运算符更易于阅读。

    MatcherAssert类支持单元测试断言。 为此,它提供了静态的assertThat(T, Matcher )方法。 传递的第一个参数是要验证的值或对象。 第二个谓词用于评估第一个谓词。

    assertThat( actual, equalTo( IN_RANGE_NUMBER ) );

    如您所见,匹配器方法模仿自然语言的流程以提高可读性。 以下代码片段更加清楚了此意图。 这使用is(Matcher )方法来修饰实际的表达式。

    assertThat( actual, is( equalTo( IN_RANGE_NUMBER ) ) );

    MatcherAssert.assertThat(...)存在另外两个签名。 首先,有一个采用布尔参数而不是Matcher参数的变量。 它的行为与Assert.assertTrue(boolean)

    第二个变体将一个附加的String传递给该方法。 这可以用来提高故障消息的表达能力:

    assertThat( "Actual number must not be equals to lower bound value.", 
                 actual, 
                 is( not( equalTo( LOWER_BOUND ) ) ) );

    在失败的情况下,给定验证的错误消息看起来像这样:

    hamcrest-failure

    Hamcrest带有一组有用的匹配器。 图书馆在线文档的“常见匹配项”部分列出了最重要的部分。 但是对于特定域的问题,如果有合适的匹配器,通常可以提高单元测试断言的可读性。

    因此,该库允许编写自定义匹配器。

    让我们返回教程的示例来讨论该主题。 首先,我们对该场景进行调整以使其更合理。 假设NumberRangeCounter.next()返回的是RangeNumber类型,而不是简单的int值:

    public class RangeNumber {
      
      private final String rangeIdentifier;
      private final int value;
    
      RangeNumber( String rangeIdentifier, int value  ) {
        this.rangeIdentifier = rangeIdentifier;
        this.value = value;
      }
      
      public String getRangeIdentifier() {
        return rangeIdentifier;
      }
      
      public int getValue() {
        return value;
      }
    }

    我们可以使用自定义匹配器来检查NumberRangeCounter#next()的返回值是否在计数器定义的数字范围内:

    RangeNumber actual = counter.next();
    
    assertThat( actual, is( inRangeOf( LOWER_BOUND, RANGE ) ) );

    适当的自定义匹配器可以扩展抽象类TypeSafeMatcher<T> 。 该基类处理null检查和类型安全。 可能的实现如下所示。 请注意,它如何添加工厂方法inRangeOf(int,int)以便于使用:

    public class InRangeMatcher extends TypeSafeMatcher<RangeNumber> {
    
      private final int lowerBound;
      private final int upperBound;
    
      InRangeMatcher( int lowerBound, int range ) {
        this.lowerBound = lowerBound;
        this.upperBound = lowerBound + range;
      }
      
      @Override
      public void describeTo( Description description ) {
        String text = format( "between <%s> and <%s>.", lowerBound, upperBound );
        description.appendText( text );
      }
      
      @Override
      protected void describeMismatchSafely(
        RangeNumber item, Description description )
      {
        description.appendText( "was " ).appendValue( item.getValue() );
      }
    
    
      @Override
      protected boolean matchesSafely( RangeNumber toMatch ) {
        return    lowerBound <= toMatch.getValue() 
               && upperBound > toMatch.getValue();
      }
      
      public static Matcher<RangeNumber> inRangeOf( int lowerBound, int range ) {
        return new InRangeMatcher( lowerBound, range );
      }
    }

    对于给定的示例,工作量可能会有些夸大。 但它显示了如何使用自定义匹配器消除先前帖子中有些神奇的IN_RANGE_NUMBER常量。 除了新类型外,还强制声明语句的编译时类型安全。 这意味着例如String参数将不被接受进行验证。

    下图显示了使用自定义匹配器的测试结果失败的样子:

    hamcrest-custom-failure

    很容易看出describeTodescribeMismatchSafely的实现以哪种方式影响故障消息。 它表示期望值应该在指定的下限和(计算的)上限1之间 ,并跟在实际值之后。

    不幸的是,JUnit扩展了其Assert类的API以提供一组assertThat(…)方法。 这些方法实际上复制了MatcherAssert提供的API。 实际上,这些方法的实现委托给这种类型的相应方法。

    尽管这似乎是一个小问题,但我认为值得一提。 由于这种方法,JUnit牢固地与Hamcrest库绑定在一起。 这种依赖性有时会导致问题。 特别是与其他库一起使用时,通过合并自己的hamcrest版本的副本,情况更糟……

    Hamcrest的单元测试主张并非没有竞争。 尽管关于每次测试一个确定每个测试 一个概念的讨论超出了本文的讨论范围,但后一种观点的支持者可能认为该库的验证声明过于嘈杂。 尤其是当一个概念需要多个断言时。

    这就是为什么我必须在本章中添加另一部分!

    断言

    在“ 测试跑步者”中,示例片段之一使用了两个assertXXX语句。 这些验证期望的异常是IllegalArgumentException的实例并提供特定的错误消息。 该段看起来像这样:

    Throwable actual = ...
    
    assertTrue( actual instanceof IllegalArgumentException );
    assertEquals( EXPECTED_ERROR_MESSAGE, actual.getMessage() );

    上一节教我们如何使用Hamcrest改进代码。 但是,如果您碰巧是该库的新手,您可能会想知道要使用哪个表达式。 或打字可能会感到不舒服。 无论如何,多个assertThat语句会加在一起。

    AssertJ库努力通过为Java提供流畅的断言来改善这一点。 流畅的接口 API的目的是提供一种易于阅读的,富有表现力的编程风格,从而减少胶合代码并简化键入。

    那么如何使用这种方法来重构上面的代码?

    import static org.assertj.core.api.Assertions.assertThat;

    与其他方法类似,AssertJ提供了一个实用程序类,该类提供了一组静态的assertThat方法。 但是这些方法针对给定的参数类型返回特定的断言实现。 这就是所谓的语句链接的起点。

    Throwable actual = ...
    
    assertThat( actual )
      .isInstanceOf( IllegalArgumentException.class )
      .hasMessage( EXPECTED_ERROR_MESSAGE );

    旁观者认为可读性在一定程度上可以扩展,但无论如何都可以用更紧凑的样式来写断言。 了解如何流畅地添加与被测特定概念相关的各种验证方面。 这种编程方法支持有效的类型输入,因为IDE的内容辅助可以提供给定值类型的可用谓词列表。

    因此,您想向后世提供表现力的失败消息吗? 一种可能性是使用describedAs作为链中的第一个链接来注释整个块:

    Throwable actual = ...
    
    assertThat( actual )
      .describedAs( "Expected exception does not match specification." )
      .hasMessage( EXPECTED_ERROR_MESSAGE )
      .isInstanceOf( NullPointerException.class );

    该代码段期望使用NPE,但假设在运行时抛出了IAE。 然后失败的测试运行将提供如下消息:

    断言失败

    也许您希望根据给定的失败原因使您的消息更加细微。 在这种情况下,您可以每个验证规范之前添加一条describedAs语句:

    Throwable actual = ...
    
    assertThat( actual )
      .describedAs( "Message does not match specification." )
      .hasMessage( EXPECTED_ERROR_MESSAGE )
      .describedAs( "Exception type does not match specification." )
      .isInstanceOf( NullPointerException.class );

    还有更多的AssertJ功能可供探索。 但是,要使这篇文章保持在范围之内,请参阅实用程序的在线文档以获取更多信息。 但是,在结束之前,让我们再次看一下范围内验证示例。 这是可以通过自定义断言解决的方法:

    public class RangeCounterAssertion
      extends AbstractAssert<RangeCounterAssertion, RangeCounter>
    {
    
      private static final String ERR_IN_RANGE_OF 
        = "Expected value to be between <%s> and <%s>, but was <%s>";
      private static final String ERR_RANGE_ID 
        = "Expected range identifier to be <%s>, but was <%s>";
      
      public static RangeCounterAssertion assertThat( RangeCounter actual ) {
        return new RangeCounterAssertion( actual );
      }
      
      public InRangeAssertion hasRangeIdentifier( String expected ) {
        isNotNull();
        if( !actual.getRangeIdentifier().equals( expected ) ) {
          failWithMessage( ERR_RANGE_ID, expected, actual.getRangeIdentifier()  );
        }
        return this;
      }
      
      public RangeCounterAssertion isInRangeOf( int lowerBound, int range ) {
        isNotNull();
        int upperBound = lowerBound + range;
        if( !isInInterval( lowerBound, upperBound ) ) {
          int actualValue = actual.getValue();
          failWithMessage( ERR_IN_RANGE_OF, lowerBound, upperBound, actualValue );
        }
        return this;
      }
    
      private boolean isInInterval( int lowerBound, int upperBound ) {
        return actual.getValue() >= lowerBound 
            && actual.getValue() < upperBound;
      }
    
      private RangeCounterAssertion( Integer actual ) {
        super( actual, RangeCounterAssertion.class );
      }
    }

    自定义断言是扩展AbstractAssert常见做法。 第一个通用参数是断言的类型本身。 流利的链接样式需要它。 第二种是断言所基于的类型。

    该实现提供了两种附加的验证方法,可以按照以下示例进行链接。 因此,这些方法将返回断言实例本身。 请注意, isNotNull()的调用如何确保我们要在其上进行断言的实际RangeNumber不为null

    定制断言由其工厂方法assertThat(RangeNumber) 。 因为它继承了可用的基本检查,所以断言可以开箱即用地验证非常复杂的规范。

    RangeNumber first = ...
    RangeNumber second = ...
    
    assertThat( first )
      .isInRangeOf( LOWER_BOUND, RANGE )
      .hasRangeIdentifier( EXPECTED_RANGE_ID )
      .isNotSameAs( second );

    为了完整RangNumberAssertion ,以下是RangNumberAssertion的实际运行方式:

    断言自定义失败

    不幸的是,不可能在同一测试用例中将两种不同的断言类型与静态导入一起使用。 当然,假定这些类型遵循assertThat(...)命名约定。 为了避免这种情况,文档建议扩展实用程序类Assertions

    这样的扩展可用于提供静态的assertThat方法,作为所有项目自定义断言的入口。 通过在整个项目中使用此自定义实用程序类,不会发生导入冲突。 在为所有断言提供单一入口点的部分中,可以找到详细的描述在线文档中有关定制断言的 yours + AssertJ

    流利的API的另一个问题是单行链接的语句可能更难调试。 这是因为调试器可能无法在链中设置断点。 此外,可能不清楚哪个方法调用已引起异常。

    但是,正如Wikipedia所说的那样,可以通过将语句分成多行来克服这些问题,如上面的示例所示。 这样,用户可以在链中设置断点,并轻松地逐行逐步执行代码。

    结论

    简而言之,JUnit的这一章介绍了不同的单元测试断言方法,例如该工具的内置机制,Hamcrest匹配器和AssertJ断言。 它概述了一些优缺点,并通过本教程的进行中示例对主题进行了扩展。 此外,还展示了如何创建和使用自定义匹配器和断言。

    尽管基于Assert的机制肯定是过时的并且不太面向对象,但它仍然具有它的提倡者。 Hamcrest匹配器将断言和谓词定义完全分开,而AssertJ断言以紧凑且易于使用的编程风格进行评分。 所以现在您选择太多了……

    请注意,这将是本教程有关JUnit测试要点的最后一章。 这并不意味着没有更多要说的了。 恰恰相反! 但这将超出此迷你系列量身定制的范围。 您知道他们在说什么: 总是让他们想要更多…

    1. 嗯,我想知道区间边界是否会比下限和范围更直观...

    翻译自: https://www.javacodegeeks.com/2014/09/junit-in-a-nutshell-unit-test-assertion.html

    展开全文
  • (收藏)JUnit 单元测试断言推荐 AssertJ.pdf
  • Spring boot 单元测试: 常用的程序测试方法有: 1、直接通过在浏览器输入网址访问; 2、通过postman工具来测试; 3、通过编写python脚本来进行测试; 但这样经常会一测就要测一整块,相对单元测试来说定位问题...

    Spring boot 单元测试:

    常用的程序测试方法有:

    1、直接通过在浏览器输入网址访问;

    2、通过postman工具来测试;

    3、通过编写python脚本来进行测试;

    但这样经常会一测就要测一整块,相对单元测试来说定位问题比较麻烦,单元测试能帮助我们拆分方法,单独测试些关键的代码、功能,是日常开发中必备的技能,同时也是各大公司招技术人员的必要要求之一。但今天看了不少文章,也试了不少,发现即是很简单的东西自己也走了不少弯路,所以觉得很有必要把今天的练习写下来。

    这里要说明一下,在单元测试里面在注入的时候,用Autowired和用new是有很大区别的

    @autowire跟XML方式注入类似,是Ioc容器负责创建新的实例,实例里可以有其他的依赖关系并也由springIoc容器负责注入,如果只是简单的new一个对象的话,新对象里面的所有变量依赖都是没有注入的,这样就失去了Ioc的意义。而且new的对象不能调用注入的其他类!!!

    详细步骤:

    Spring boot目录结构一般是固定的:

    选中要测试的方法体右键:

    会弹出新建测试类的框

    上面一个是我测试建的,如果要新建应该选第二个:Create New Test...

    上面是生成测试类的名称以及测试的一些属性,下面是选择你要放入的待测试的模块:

    下面开始上代码:

    Spring boot自带的测试基类(启动配置):

    package com.ai.rai.group.workflow.application;
    
    import org.junit.runner.RunWith;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    /**
     * Project:bean_generator
     * Author:hangke
     * Date:2017/1/13
     * Description://TODO add description here
     */
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = TestApplication.class)
    public class BaseTestApplication
    {
        protected Logger logger = LoggerFactory.getLogger(this.getClass());
    }
    

    测试类启动入口(一般都有的,这边也贴一下吧):

    package com.ai.rai.group.workflow.application;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    
    /**
     * Project:html_template
     * Author:hangke
     * Date:2017/3/22
     * Description://TODO add description here
     */
    @SpringBootApplication
    @ComponentScan(basePackages={"com.ai", "com.asiainfo"})
    public class TestApplication
    {
        public static void main(String[] args)
        {
            SpringApplication.run(TestApplication.class,args);
        }
    }
    

    待测试的Service方法就不贴了,根据自己的实际情况写逻辑就行;

    测试类(使用依赖注入):

    package com.ai.rai.group.workflow.service.impl;
    
    import com.ai.rai.group.workflow.application.BaseTestApplication;
    import com.ai.rai.group.workflow.entity.beans.TmWorkInst;
    import com.ai.rai.group.workflow.service.IWorkFlowService;
    import org.apache.commons.collections.bag.SynchronizedSortedBag;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import sun.tools.jar.Main;
    
    import java.util.List;
    
    import static org.junit.Assert.*;
    @RunWith(SpringJUnit4ClassRunner.class)
    public class WorkFlowServiceImpllswTest extends BaseTestApplication {
    
        //Service里面Dao用了依赖注入,这便不能用new对象的方式
        @Autowired
        IWorkFlowService workFlowServiceImpl;
    
        @Test
        public void selectWorkInstById() {
            //这个用的之前写好的Service测试,
            TmWorkInst workInst = new TmWorkInst();
            List<TmWorkInst> ss = workFlowServiceImpl.selectWorkInstById(workInst);
            for (TmWorkInst o : ss)
                System.out.println("==============="+o.toString());
        }
    }

     控制台部分信息打印:

    只是想学习自己简单调试下接口的同学,到这可以下课了;

    正常的单元测试流程还需要加断言打印日志

    加断言可以避免对数据库的误操作,以及缩短测试流程(断言失败就不再执行之后的代码了)

    代码:

    package com.ai.rai.group.workflow.service.impl;
    
    import com.ai.rai.group.workflow.application.BaseTestApplication;
    import com.ai.rai.group.workflow.entity.beans.TmWorkInst;
    import com.ai.rai.group.workflow.service.IWorkFlowService;
    import org.apache.commons.collections.bag.SynchronizedSortedBag;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import sun.tools.jar.Main;
    
    import java.util.List;
    
    import static org.junit.Assert.*;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    public class WorkFlowServiceImpllswTest extends BaseTestApplication {
    
        //Service里面Dao用了依赖注入,这便不能用new对象的方式
        @Autowired
        IWorkFlowService workFlowServiceImpl;
    
        @Test
        public void selectWorkInstById() {
            TmWorkInst workInst = new TmWorkInst();
            List<TmWorkInst> ss = workFlowServiceImpl.selectWorkInstById(workInst);
            //添加断言(判空) 
            assertNull(ss);
            for (TmWorkInst o : ss)
                System.out.println("===============" + o.toString());
        }
    }

    查看日志:

    断言失败后后面的代码就没再执行了,比较正规和安全;

    附上单元测试断言汇总:

    出自:https://my.oschina.net/u/1418722/blog/340194

    XCTFail(...)
    
    任何尝试都会测试失败,...是输出的提示文字。(后面都是这样)
    
    XCTAssertNil(expression, ...)
    
    expression为空时通过,否则测试失败。
    
    expression接受id类型的参数。
    
    
    
    XCTAssertNotNil(expression, ...)
    
    expression不为空时通过,否则测试失败。
    
    expression接受id类型的参数。
    
    
    
    
    XCTAssert(expression, ...)
    
    
    expression为true时通过,否则测试失败。
    
    expression接受boolean类型的参数。
    
    
    
    
    XCTAssertTrue(expression, ...)
    
    
    expression为true时通过,否则测试失败。
    
    expression接受boolean类型的参数。
    
    
    
    
    XCTAssertFalse(expression, ...)
    
    
    expression为false时通过,否则测试失败。
    
    expression接受boolean类型的参数。
    
    
    
    XCTAssertEqualObjects(expression1, expression2, ...)
    
    expression1和expression1地址相同时通过,否则测试失败。
    
    expression接受id类型的参数。
    
    
    
    XCTAssertNotEqualObjects(expression1, expression2, ...)
    
    expression1和expression1地址不相同时通过,否则测试失败。
    
    expression接受id类型的参数。
    
    
    
    
    XCTAssertEqual(expression1, expression2, ...)
    
    
    expression1和expression1相等时通过,否则测试失败。
    
    expression接受基本类型的参数(数值、结构体之类的)。
    
    
    
    XCTAssertNotEqual(expression1, expression2, ...)
    
    expression1和expression1不相等时通过,否则测试失败。
    
    expression接受基本类型的参数。
    
    
    
    XCTAssertEqualWithAccuracy(expression1, expression2, accuracy, ...)
    
    expression1和expression2之间的任何值都大于accuracy时,测试失败。
    
    expression1、expression2、accuracy都为基本类型。
    
    
    
    XCTAssertNotEqualWithAccuracy(expression1, expression2, accuracy, ...) 
    
    expression1和expression2之间的任何值都小于等于accuracy时,测试失败。
    
    expression1、expression2、accuracy都为基本类型。
    
    
    
    XCTAssertGreaterThan(expression1, expression2, ...)
    
    expression1 <= expression2时,测试失败。
    
    expression为基本类型
    
    
    
    
    
    
    XCTAssertGreaterThanOrEqual(expression1, expression2, ...)
    
    
    expression1 < expression2时,测试失败。
    
    expression为基本类型
    
    
    
    
    XCTAssertLessThan(expression1, expression2, ...)
    
    
    expression1 >= expression2时,测试失败。
    
    expression为基本类型
    
    
    
    
    XCTAssertLessThanOrEqual(expression1, expression2, ...)
    
    
    expression1 > expression2时,测试失败。
    
    expression为基本类型
    
    
    
    XCTAssertThrows(expression, ...)
    
    expression没抛异常,测试失败。
    
    expression为一个表达式
    
    
    
    XCTAssertThrowsSpecific(expression, exception_class, ...)
    
    expression没抛指定类的异常,测试失败。
    
    expression为一个表达式
    
    exception_class为一个指定类
    
    
    
    
    XCTAssertThrowsSpecificNamed(expression, exception_class, exception_name, ...)
    
    
    expression没抛指定类、指定名字的异常,测试失败。
    
    expression为一个表达式
    
    exception_class为一个指定类
    
    exception_name为一个指定名字
    
    
    
    
    XCTAssertNoThrow(expression, ...)
    
    expression抛出异常时,测试失败。
    
    expression为一个表达式
    
    
    
    
    XCTAssertNoThrowSpecific(expression, exception_class, ...)
    
    
    expression抛出指定类的异常,测试失败。
    
    expression为一个表达式
    
    
    
    
    XCTAssertNoThrowSpecificNamed(expression, exception_class, exception_name, ...)
    
    
    expression抛出指定类、指定名字的异常,测试失败。
    
    expression为一个表达式
    
    exception_class为一个指定类
    
    exception_name为一个指定名字
    
    

    参考资料:

    https://www.cnblogs.com/shunyang/p/8681111.html

    https://www.cnblogs.com/aston/p/7259825.html

    https://www.cnblogs.com/dyh2025/p/9251144.html

    展开全文
  • 单元测试断言汇总

    千次阅读 2016-06-08 14:50:29
    ...任何尝试都会测试失败,...是输出的提示文字。(后面都是这样) XCTAssertNil(expression, ...) expression为空时通过,否则测试失败。 expression接受id类型的参数。 XCT
  • Junit 单元测试断言详解

    千次阅读 2018-06-13 20:35:56
    本文只讲解junit断言的部分,注解部分请参考:...
  • 单元测试断言利器 AssertJ

    千次阅读 2016-09-07 16:33:02
    // 断言字符串相等 断言忽略大小写判断字符串相等    assertThat( "Frodo" ).isEqualTo( "Frodo" ).isEqualToIgnoringCase( "frodo" );    // 断言开始字符串 结束字符穿 字符串长度    ...
  • JUnit 单元测试断言推荐 AssertJ

    千次阅读 2016-02-01 17:52:48
    前言 由于JUnit的Assert是公认的烂API,所以不推荐使用,目前推荐使用的是AssertJ。 AssertJ网站: http://joel-costigliola.github.io/assertj/ ...github上examples 地址 ...JUnit 单元测试断言推荐 AssertJ
  • 在spring 项目中主要使用Junit框架来实现单元测试,而单元测试中最多用到的就是断言,通过断言来判断程序是否符合预期。 JUnit 中的断言 Junit所有的断言都包含在 Assert 类中。 这个类提供了很多有用的断言方法来...
  • 单元测试断言库:chai.js中文文档

    千次阅读 2019-02-15 14:31:49
    它们以相同的链式结构进行断言,它们的不同之处在于初始的断言构造。可以查看风格指南来进行比较。 API参照: 链式语言结构 使用以下的链式供给(getter)来提高你断言的可阅读性啊。 Chains to be been is that ...
  • 单元测试断言

    2016-09-05 12:20:55
    1.在Xcode 5中新建一个工程默认自带一个单元测试的文件夹,IDE自动生成了一个实现XCTestCase的.m文件,里面有一个失败测试(早期版本中实现的是SenTestCase,是苹果集成的第三方的,现在苹果建议使用新的XCTestCase)...
  • 单元测试 JUnit 断言 注解 mock Unitils spring 注解
  • 单元测试常用断言方法
  • 过时 - 请继续使用新的 repo:https://github.com/nasa/cFE/tree/master/tools/ut_assert 该项目将在不久的将来关闭核心飞行系统 (cFS) 单元测试库,提供框架和集合旨在促进任务和应用程序基于断言单元测试的实用...
  • 1、单元测试的几个重要概念 (1)Test Case 一个Test Case实例是一个测试用例,完整的测试流程包括测试前准备环境的搭建(setUp)、实现测试过程的代码(run)、以及 测试后环境的还原(tearDown)。 (2)Test ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,879
精华内容 14,351
关键字:

单元测试断言测试