Should it be “Arrange-Assert-Act-Assert”?

后端 未结 14 2407
不思量自难忘°
不思量自难忘° 2020-11-30 19:53

Regarding the classic test pattern of Arrange-Act-Assert, I frequently find myself adding a counter-assertion that precedes Act. This way I know that the passing assertion

相关标签:
14条回答
  • 2020-11-30 20:35

    If you really want to test everything in the example, try more tests... like:

    public void testIncludes7() throws Exception {
        Range range = new Range(0, 5);
        assertFalse(range.includes(7));
    }
    
    public void testIncludes5() throws Exception {
        Range range = new Range(0, 5);
        assertTrue(range.includes(5));
    }
    
    public void testIncludes0() throws Exception {
        Range range = new Range(0, 5);
        assertTrue(range.includes(0));
    }
    
    public void testEncompassInc7() throws Exception {
        Range range = new Range(0, 5);
        range.encompass(7);
        assertTrue(range.includes(7));
    }
    
    public void testEncompassInc5() throws Exception {
        Range range = new Range(0, 5);
        range.encompass(7);
        assertTrue(range.includes(5));
    }
    
    public void testEncompassInc0() throws Exception {
        Range range = new Range(0, 5);
        range.encompass(7);
        assertTrue(range.includes(0));
    }
    

    Because otherwise you are missing so many possibilities for error... eg after encompass, the range only inlcudes 7, etc... There are also tests for length of range (to ensure it didn't also encompass a random value), and another set of tests entirely for trying to encompass 5 in the range... what would we expect - an exception in encompass, or the range to be unaltered?

    Anyway, the point is if there are any assumptions in the act that you want to test, put them in their own test, yes?

    0 讨论(0)
  • 2020-11-30 20:36

    I don't use that pattern, because I think doing something like:

    Arrange
    Assert-Not
    Act
    Assert
    

    May be pointless, because supposedly you know your Arrange part works correctly, which means that whatever is in the Arrange part must be tested aswell or be simple enough to not need tests.

    Using your answer's example:

    public void testEncompass() throws Exception {
        Range range = new Range(0, 5);
        assertFalse(range.includes(7)); // <-- Pointless and against DRY if there 
                                        // are unit tests for Range(int, int)
        range.encompass(7);
        assertTrue(range.includes(7));
    }
    
    0 讨论(0)
  • 2020-11-30 20:37

    Tossing in a "sanity check" assertion to verify state before you perform the action you're testing is an old technique. I usually write them as test scaffolding to prove to myself that the test does what I expect, and remove them later to avoid cluttering tests with test scaffolding. Sometimes, leaving the scaffolding in helps the test serve as narrative.

    0 讨论(0)
  • 2020-11-30 20:38

    I am now doing this. A-A-A-A of a different kind

    Arrange - setup
    Act - what is being tested
    Assemble - what is optionally needed to perform the assert
    Assert - the actual assertions
    

    Example of an update test:

    Arrange: 
        New object as NewObject
        Set properties of NewObject
        Save the NewObject
        Read the object as ReadObject
    
    Act: 
        Change the ReadObject
        Save the ReadObject
    
    Assemble: 
        Read the object as ReadUpdated
    
    Assert: 
        Compare ReadUpdated with ReadObject properties
    

    The reason is so that the ACT does not contain the reading of the ReadUpdated is because it is not part of the act. The act is only changing and saving. So really, ARRANGE ReadUpdated for assertion, I am calling ASSEMBLE for assertion. This is to prevent confusing the ARRANGE section

    ASSERT should only contain assertions. That leaves ASSEMBLE between ACT and ASSERT which sets up the assert.

    Lastly, if you are failing in the Arrange, your tests are not correct because you should have other tests to prevent/find these trivial bugs. Because for the scenario i present, there should already be other tests which test READ and CREATE. If you create a "Guard Assertion", you may be breaking DRY and creating maintenance.

    0 讨论(0)
  • 2020-11-30 20:41

    An Arrange-Assert-Act-Assert test can always be refactored into two tests:

    1. Arrange-Assert
    

    and

    2. Arrange-Act-Assert
    

    The first test will only assert on that which was set up in the Arrange phase, and the second test will only assert for that which happened in the Act phase.

    This has the benefit of giving more precise feedback on whether it's the Arrange or the Act phase that failed, while in the original Arrange-Assert-Act-Assert these are conflated and you would have to dig deeper and examine exactly what assertion failed and why it failed in order to know if it was the Arrange or Act that failed.

    It also satisfies the intention of unit testing better, as you are separating your test into smaller independent units.

    Lastly, keep in mind that whenever you see similar Arrange sections in different test, you should try to pull these out into shared helper methods, so that your tests are more DRY and more maintainable in the future.

    0 讨论(0)
  • 2020-11-30 20:44

    I have done this before when investigating a test that failed.

    After considerable head scratching, I determined that the cause was the methods called during "Arrange" were not working correctly. The test failure was misleading. I added a Assert after the arrange. This made the test fail in a place which highlighted the actual problem.

    I think there is also a code smell here if the Arrange part of the test is too long and complicated.

    0 讨论(0)
提交回复
热议问题