[S01E02] iOS unit test

what: What is unit testing
how: How to conduct unit tests
why: Why unit testing

1, What is unit testing

1. Unit test

Unit can be simply understood as a method, so unit testing is testing for methods

2. The position of unit test in the test

Generally speaking, small tests are used to ensure code quality, and medium and large tests are used to ensure product quality.

The test pyramid depends more and more from bottom to top, but our confidence in the quality of the whole product is stronger and stronger. We need to provide mock environment for unit testing.
Generally, unit testing does not depend on any external environment, network environment or database environment.

Unit testing is the cornerstone of the entire test pyramid. It occupies a very important position in the whole test.

Reference: the way of Google software testing

2, How to conduct unit tests

2.1 how to add a unit test Scheme to a project

If we don't check include Tests when creating the project, we need to add a test Scheme to the project

2.2 add a test case

Under the new DemoTests, click DemoTests A new test case is added in M. the test case must be an instance method, the return value is void and starts with test

- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
}

2.3 common assertions

XCTFail(@"Fail");
XCTAssertNil(@"not nil string", @"string must be nil");
XCTAssertNotNil(@"not nil string", @"string can not be nil");
XCTAssert((2 > 2), @"expression must be true");
XCTAssertTrue(1, @"Can not be zero");
XCTAssertFalse((2 < 2), @"expression must be false");
XCTAssertEqualObjects(@"1", @"1", @"[a1 isEqual:a2] should return YES");
XCTAssertNotEqualObjects(@"1", @"2", @"[a1 isEqual:a2] should return NO");
XCTAssertEqual(1, 2, @"a1 = a2 shoud be true"); 
XCTAssertEqual(str1, str2, @"a1 and a2 should point to the same object"); 

2.4 execution sequence of each function in the test file

The console outputs the following results

2021-07-08 14:29:10.782883+0800 Demo[19597:31722127] 🍺 +setUp
2021-07-08 14:29:10.784474+0800 Demo[19597:31722127] 🍺 -setUp
2021-07-08 14:29:10.784667+0800 Demo[19597:31722127] 🍺 testExample1
2021-07-08 14:29:10.784857+0800 Demo[19597:31722127] 🍺 -tearDown
2021-07-08 14:29:10.785971+0800 Demo[19597:31722127] 🍺 -setUp
2021-07-08 14:29:10.786121+0800 Demo[19597:31722127] 🍺 testExample2
2021-07-08 14:29:10.786270+0800 Demo[19597:31722127] 🍺 -tearDown
2021-07-08 14:29:10.787399+0800 Demo[19597:31722127] 🍺 -setUp
2021-07-08 14:29:10.787548+0800 Demo[19597:31722127] 🍺 testExample3
2021-07-08 14:29:10.787700+0800 Demo[19597:31722127] 🍺 -tearDown
2021-07-08 14:29:10.788271+0800 Demo[19597:31722127] 🍺 +tearDown

It is worth noting that testExample1, testExample2 and testExample3 are not executed in a certain order.

2.2 how to view unit test coverage in Xcode

The coverage of unit tests is turned off by default and needs to be turned on in Xcode


3, Why unit testing

  • Check code logic

  • Verify boundary conditions

  • Ensure the smooth progress of reconstruction

3.1 check code logic

The most basic function of unit testing is to verify whether the logic of our function meets the expectations.

3.2 verification of boundary conditions

The number of likes displays the string conversion. For example, the real number of likes in this video is 23848999, and we display 2384.8

///Convert the number of likes into a string
///@ param count number of likes
///When the @ discussion number is greater than or equal to 10000, one decimal point shall be reserved
///@ discussion when the number is less than 10000, show the real number
- (NSString *)descForLikeCount:(NSInteger)count{
    NSString *desc = nil;
    if (count >= 10000) {
        desc = [NSString stringWithFormat:@"%.1f ten thousand", (count / 10000.f)];
    }else{
        desc = [NSString stringWithFormat:@"%ld", count];
    }
    return desc;
}

At first glance, the above function does not have any problem, but when we enter 99999, the return value of the function is 100000, which does not meet our expectations. Such errors are difficult to find even in Code Review.

3.3 ensure the smooth progress of reconstruction

Define direction enumeration

typedef enum{

    DIREDRTION_UNKNOW = -1, // Unknown direction
    DIREDRTION_N = 0, // north
    DIREDRTION_E = 1, // east
    DIREDRTION_S = 2, // south
    DIREDRTION_W = 3 // west

}DIREDRTION;

Functions before Refactoring:

- (DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
    if ([cmd isEqualToString:@"L"]){
        if (curDirection == DIREDRTION_N) {
            return DIREDRTION_W;
        }
        if (curDirection == DIREDRTION_W) {
            return DIREDRTION_S;
        }
        if (curDirection == DIREDRTION_S) {
            return DIREDRTION_E;
        }
        if (curDirection == DIREDRTION_E) {
            return DIREDRTION_N;
        }
    }else if ([cmd isEqualToString:@"R"]){
        if (curDirection == DIREDRTION_N) {
            return DIREDRTION_E;
        }
        if (curDirection == DIREDRTION_W) {
            return DIREDRTION_N;
        }
        if (curDirection == DIREDRTION_S) {
            return DIREDRTION_W;
        }
        if (curDirection == DIREDRTION_E) {
            return DIREDRTION_S;
        }
    }
    return  DIREDRTION_UNKNOW;
}

At this time, we need some unit tests as a guarantee

With the deepening of our work, we found a law

Left turn: final direction = (current direction + 3)% 4

Turn right: final direction = (current direction + 5)% 4
Reconstructed function:

- (DIREDRTION)turn:(NSString *)cmd curDirection:(DIREDRTION)curDirection{
    if ([cmd isEqualToString:@"L"]) {
        DIREDRTION direction = (curDirection + 3) % 4;
        return direction;
    }
    if ([cmd isEqualToString:@"R"]) {
        DIREDRTION direction = (curDirection + 5) % 4;
        return direction;
    }
    return  DIREDRTION_UNKNOW;
}

After the refactoring is complete, run the unit tests. Still all passed. It is proved that this refactoring does not introduce new problems.

So far, I think you have been a strong supporter of unit testing. However, as we said before, unit testing does not depend on network environment, system time and database, but simply tests function.


These materials should be the most comprehensive and complete war preparation warehouse for [software testing] friends. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you! Everything should be done as soon as possible, especially in the technology industry. We must improve our technical skills.

Pay attention to my WeChat official account.

Software testing technology exchange group: 902061117 peer Daniel exchange, learning and puzzle solving!

If my blog is helpful to you and you like my blog content, please click "like", "comment" and "collect" for three times!

Keywords: Python Programmer unit testing software testing Stress testing

Added by tmed on Fri, 14 Jan 2022 02:55:39 +0200