What is unit Testing

Unit Testing, also known as module Testing, is a test work designed to check the correctness of program module software. A program unit is the smallest testable part of an application. For object-oriented programming, the smallest unit is a method, including a method in a base class, an abstract class, or a derived class. Ideally, each test case is independent of the others, and modules are isolated when tested. Unit tests are typically written by software developers to ensure that the code they write matches the software requirements and complies with development goals. It can be implemented manually or as part of build automation. Unit tests allow programmers to refactor the code in the future and ensure that the module still works correctly. The process is to write unit tests for all methods so that if a change causes an error to occur, it can be quickly located and fixed. Readable unit tests make it easy for programmers to check that pieces of code are still working. Well-designed unit test cases cover all paths of program unit branches and loop conditions. In a continuous unit test environment, unit tests can continue to be used to accurately reflect the performance of executable programs and code when any changes occur, through their inherent ongoing maintenance efforts. With the aforementioned development practices and coverage of unit tests, accuracy can always be maintained.

The purpose of unit testing

1. Ensure code quality

Code can be checked syntactically by the compiler, but there is no guarantee that the code logic is correct, especially if many unit branches are involved. Unit tests ensure that the code behaves as expected and as expected. When testing whether a piece of code behaves as you expect it to, you need to make sure that the code behaves as you expect it to under any circumstances, such as possible null arguments, possible asynchronous operations, and so on.

2. Keep your code maintainable

If the original unit tests are correct, the test results should be correct no matter how the internal code of the unit is modified, and the modification will not affect other modules.

3. Make your code extensible

To ensure viable and sustainable unit testing, program units should be low-coupled, otherwise unit testing will be difficult.

The nature of unit testing

1. It is an act of validation

Unit tests verify the correctness of the code logic in the early stage of development, and test results provide quantifiable guarantee for all of them in the later stage of development, whether it is modification of the internal code or refactoring.

2. It is a design behavior

In order to do unit testing, especially by writing unit tests (TDD) first, we will think in terms of the caller, in terms of the interface, and we must design the program units in such a way that the interface functions are clearly separated, easily tested, and as little coupling with external modules as possible.

3. It’s a quick way back

Unit testing is a fast, reliable way to develop and modify functionality from source code.

4. Excellent documentation of the program

In effect, unit tests are like executable documentation of what you can expect the code to do when you call it with various conditions.

—————————————–

* Two test ideas

Test-driven Development (TDD) is an application method in the software development process. It was advocated by Extreme programming and is named for its idea of first writing Test programs and then coding to implement their functions. Test-driven development is a way of thinking with two hats on: first, put on the hat to implement the function, with the help of testing, quickly implement its function; Put on the refactoring hat and improve code quality by removing redundant code under the protection of testing. Testing drives the entire development process: first, it drives the design of the code and the implementation of the function; After that, the driver code is redesigned and refactored.

Behavior-driven Development (BDD) is an agile software development technique. BDD focuses on obtaining a clear understanding of expected software Behavior through discussions with stakeholders. It extends the test-driven development approach (TDD) by writing non-programmer-readable test cases in natural language. This allows developers to focus on how the code should be written, rather than on technical details, and minimizes the cost of translating back and forth between the technical language of the code writer and the domain language of business customers, users, stakeholders, project managers, and so on.

Kiwi stands for BDD in the iOS unit testing framework.

—————————————–

A first look at iOS unit testing

XCTest

Xcode integrates unit testing support, Xcode4. x integrates OCUnit, and was upgraded to XCTest in xcode5. x era. XCTest can also do UI testing in xcode7.x era. Let’s take a quick look at the use of XCTest. In a new Xcode project, a unit test target is created by default and a test case class is created that inherits from XCTestCase

ASUnitTestFirstDemoTests.m
#import <XCTest/XCTest.h>
#import "ASRevenueBL.h"

@interface ASUnitTestFirstDemoTests : XCTestCase

@property (nonatomic, strong) ASRevenueBL *revenueBL;

@end

@implementation ASUnitTestFirstDemoTests

- (void)setUp {
    [super setUp];
    self.revenueBL = [[ASRevenueBL alloc] init];
    // Put setup code here. This method is called before the invocation of each test method in the class.
}

- (void)tearDown {
    self.revenueBL = nil;

    // Put teardown code here. This method is called after the invocation of each test method in the class.
    [super tearDown];
}

- (void)testLevel1 {// Async test double Revenue = 5000; double tax = [self.revenueBL calculate:revenue]; @ XCTAssertEqual (tax, 45.0"Use Case 1 test failed"); XCTAssertTrue (tax = = 45.0); } - (void)testLevel2 {
  XCTestExpectation *exp = [self expectationWithDescription:@"Timeout"]; NSOperationQueue *queue = [[NSOperationQueue alloc]init]; [queue addOperationWithBlock:^{ double revenue = 1500; double tax = [self.revenueBL calculate:revenue]; sleep(1); @ XCTAssertEqual (tax, 45.0"Use Case 2 test failed"); [exp fulfill]; }]; [selfwaitForExpectationsWithTimeout:3 handler:^(NSError * _Nullable error) {
    if (error) {
      NSLog(@"Timeout Error: %@", error); }}]; } - (void)testPerformanceExample {
    // This is an example of a performance test case.
    [self measureBlock:^{
        // Put the code you want to measure the time of here.
       for (int a = 0; a<10000; a+=a) {
            NSLog(@"%zd", a); }}]; } @endCopy the code
ASRevenueBL.m
#import "ASRevenueBL.h"

# define baseNum 3500.0@implementation ASRevenueBL - (double)calculate:(double)revenue {double tax = 0.0; double dbTaxRevenue = revenue - baseNum;if(dbTaxRevenue <= 1500) {tax = dbTaxRevenue * 0.03; }else if(dbTaxRevenue > 1500 && dbTaxRevenue <= 4500) {tax = dbTaxRevenue * 0.1-105; }else if(dbTaxRevenue > 4500&&dbTaxRevenue <= 9000) {tax = dbTaxRevenue * 0.2-555; }else if(dbTaxRevenue > 9000 &&dbTaxRevenue <= 35000) {tax = dbTaxRevenue * 0.25-1005; }else if(dbTaxRevenue > 35000 &&dbTaxRevenue <= 55000) {tax = dbTaxRevenue * 0.3-2755; }else if(dbTaxRevenue > 5000&&dbTaxRevenue <= 80000) {tax = dbTaxRevenue * 0.35-5505; }else if(dbTaxRevenue > 80000) {tax = dbTaxRevenue * 0.45-13505; }return tax;
}

@end
Copy the code
XCTest:
 - (void)setUp; // Call it before the test starts to initialize some objects and variables - (void)tearDown; // call - (void)test##Name; // All methods with the test prefix that have no arguments and no return are a test method- (void)measureBlock:((void (^)(void)))block; // Measure execution time - (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(nullable XCWaitCompletionHandler)handler; How many seconds / / exception not fullfill error - expectationForNotification: (NSNotificationName) notificationName XCTestExpectation (*) object:(nullable id)objectToObserve handler:(nullable XCNotificationExpectationHandler)handler; // match to notice fullfill - (XCTestExpectation *)expectationForPredicate:(NSPredicate *)predicate evaluatedWithObject:(id)object handler:(nullable XCPredicateExpectationHandler)handler; / / predicate returnstrueTest fullfill...Copy the code
The test results

Product-test or command + u starts test

** XCTAssertNil(a1,...) If expression is null, XCTAssert(expression,...) Pass when expression is TRUE. XCTAssertTrue(expression, format...) Pass when expression is TRUE. XCTAssertEqual(e1, e2, ...) E1 == e2 passes; XCTAssertThrows(expression, format...) Pass when expression throws an exception; XCTAssertThrowsSpecific(expression, specificException, format...) Pass when expression throws a specificException exception;Copy the code

TestLevel1 The tax calculated by revenueBL is the same as expected, and the test passes. RevenueBL tax calculated by testLevel2 is different from expected. The test fails, reflecting some logic flaws of the program. The average execution time in the testPerformanceExample is lower than the benchmark value and the test passes.

The command line

Tests can also be launched from the command line for continuous integration.

Assuner$ cd Desktop/
Desktop Assuner$ cd ASUnitTestFirstDemo/
ASUnitTestFirstDemo Assuner$ xcodebuild test -project ASUnitTestFirstDemo.xcodeproj -scheme ASUnitTestFirstDemo -destination 'platform = iOS Simulator, OS = 11.0, name = iPhone 7'// There can be multiple destinationsCopy the code

The results of

Test Suite 'All tests'Started at 2017-09-11 11:12:16.348 Test Suite'ASUnitTestFirstDemoTests.xctest'Started at 2017-09-11 11:12:16.349 Test Suite'ASUnitTestFirstDemoTests'349 Test Case Started at 2017-09-11 11:12:16.349'-[ASUnitTestFirstDemoTests testLevel1]' started.
Test Case '-[ASUnitTestFirstDemoTests testLevel1]'Passed (0.001 seconds). Test Case'-[ASUnitTestFirstDemoTests testLevel2]'started. /Users/liyongguang-eleme-iOS-Development/Desktop/ASUnitTestFirstDemo/ASUnitTestFirstDemoTests/ASUnitTestFirstDemoTests.m :46: error: -[ASUnitTestFirstDemoTeststestLevel2] : ((tax) equal to (45.0)) Failed:"60") is not equal to ("45") - Test Case of use Case 2 failed'-[ASUnitTestFirstDemoTests testLevel2]'Failed (1.007 seconds).test Suite'ASUnitTestFirstDemoTests'Executed 2 tests, with 1 failure (0 unexpected)in 1.008 (1.009) seconds
Test Suite 'ASUnitTestFirstDemoTests.xctest'Failed at 2017-09-11 11:12:17.359. Executed 2 tests, with 1 failure (0 unexpected)in 1.008 (1.010) seconds
Test Suite 'All tests'Executed 2 tests, with 1 failure (0 unexpected)in 1.008 (1.012) seconds
Failing tests:
	-[ASUnitTestFirstDemoTests testLevel2]
** TEST FAILED **

Copy the code

If this is the workspace

xcodebuild -workspace ASKiwiTest.xcworkspace -scheme ASKiwiTest-Example -destination 'platform = iOS Simulator, OS = 11.0, name = iPhone 7' test
Copy the code

Each test method is run and given a description of the results.

Thanks for watching! Please correct me if there are any mistakes

Refer to the reading

Man XcodeBuild XCTestCase cocoaChina