The road to APP refactoring: introducing unit testing

1. Why introduce unit testing

During the development process, we will encounter such problems:

  • When faced with the need to refactor huge module code, there is no way to start
  • Modified one place but caused a new bug in another place
  • Extending new features and causing bugs in old code
  • There is a bug in the basic function interface that is difficult for testers to cover
  • A bug triggered by special boundary conditions that is difficult to reproduce has appeared

In addition, we may also encounter some such modules:

  • Module A depends on the results of Module B, but Module B has not been developed yet
  • Module status is too complicated, manual testing takes a lot of time
  • Module business is related to time nodes, and manual testing is difficult to cover

At this time, we may be able to use experience and rich debugging skills to solve these problems, but in many cases our processing is not perfect, because we lack a specification and it is difficult to take into account the impact of other modules in the coding process. At this time, we need Introduce unit testing .

Second, the value of unit testing

Improved maintainability

When modifying the code, using unit testing can clearly know whether the old business logic is destroyed, which greatly reduces the possibility of regression errors. And when we get a bug from the test, we can use the test case to restore it. When our test passes, the bug will be solved, and the bug fix test case also ensures that the bug will not be repeated in the future appear.

Reduce the difficulty of reconstruction

With the guarantee of unit testing, we can boldly carry out refactoring designs, and unit testing will also become a good mold for refactoring. Of course, unit tests need to be refactored during refactoring, but compared with reliability, this extra burden is worth bearing

Reduce debugging time

In debugging, we often need to spend some extra time to trigger the code that needs to be debugged, but in unit testing, we can build related test cases for the code that needs to be debugged, and it is convenient for repeated testing and simulation. Reduce the debugging time.

Reduce low-level errors

The value of testing is to find and solve errors for us. This is especially true for unit testing. When we unit test our own code, we can easily eliminate some very low-level errors. At least we can guarantee that in some normal situations The following code can work correctly.

Description code

Good code is a good document, especially unit testing. A good unit test can describe how the code should behave as expected under the corresponding situation, so others only need to look at the test case to clearly know the function of the code.

Improve code quality

If a code is strongly coupled with other codes, it is difficult to be tested, so for testing, developers will be driven to write low-coupling and extensible code.

Three, unit test plan

There are two ideas in unit testing: Test Driven Development (TDD) and Behavior Driven Development (BDD)

Test Driven Development (TDD)

  • Write test cases first according to requirements and interfaces
  • Write business code based on test cases
  • Low development efficiency
  • Resource intensive
  • High test coverage

Behavior Driven Development (BDD)

  • Describe code behavior through test cases
  • Quick feedback by running test cases automatically
  • Use Mock as a stand-in for related code modules
  • High development efficiency
  • Low resource consumption
  • Test coverage is lower than TDD

Based on the current project situation and development process, I chose BDD as the testing framework, and will choose to use the XCTest + OCMock + OCHamcrest solution. The following is an introduction to the three frameworks:

XCTest

What XCTest can accomplish

  • Logical judgment of basic assertion
  • Asynchronous testing
  • Performance Testing

Why choose XCTest

  • XCode's native testing framework can better adapt to Apple's future updates
  • XCTest has a large amount of documentation support, and it is less difficult to get started
  • XCTest is only used as a project testing framework after being added to the project, and will not affect packaging and other things

OCMock

Why do I need OCMock

Mock is simulation, OCMock can fake (simulate) an object, give it some preset values ​​and the like, and perform corresponding verification

For example, when I need to test the WiFi direct connection module, I need a WiFi to test the direct connection function. At this time, we can use OCMock to simulate a WiFi object, which can be simulated as risky WiFi or as free WiFi. In this way, the test of our direct connection module is completely independent of the WiFi object and can be easily tested.

What OCMock can accomplish

  • Create a simulation object, simulate the behavior of a specific object, and exclude some external interference
  • Construct your own use case for verification
  • Redefine existing methods and interact with logic defined by yourself
  • Determine whether the function has been executed

Why choose OCMock

  • Native XCTest does not support mock function
  • OCMock is an open source project dedicated to mock testing for iOS and OS X. It has more than 5000+ apps and 11 million+ downloads.
  • OCMock uses the Apache 2.0 protocol, can modify the code when needed to meet the needs and release/sell as an open source or commercial product
  • OCMock has official documents with complete information

OCHamcrest

What OCHamcrest can accomplish

  • Higher-level assertions
  • Assertion scalability
  • Support structure assertions

Why choose OCHamcrest

  • Compared to another assertion framework, Expecta, OCHamcrest is more mature, and Expecta may cause incorrect assertion results
  • XCTest's built-in assertions are not sufficient, and judgment under complex conditions requires writing a lot of assertion code
  • Use OCHamcrest's extensibility to output formatted custom log to log file, providing more customized and detailed information

Fourth, the overall test framework

Five, what should be tested and how should be tested

Principles of testing

  • Fast : so that you don’t mind running
  • Independent : One test should not be coupled to another test
  • Repeatable : the results of each test should be consistent
  • Verifiable : the result should be success/failure, not an explanatory log document

What to test

Before writing any tests, you should clarify what should be tested. In general, unit tests should include these:

  • Core function test
  • Boundary conditions
  • Error handling

Ideas for testing

In view of the current project situation, I will use a combination of unit testing and manual testing, because most of our functions are currently involved in the UI, and we cannot completely rely on unit testing to complete all the testing work, but we can integrate the logic part For separation, take the network connection module as an example:

Request process

We can disassemble some of the tests related to the interface and implement unit tests in the logic part, while the manual test part simply checks whether the entire direct connection process and the interface part are normal.

This can avoid relying on logic during manual testing. For example, when you need to test whether the module will have a problem after 100 requests are initiated, you don’t need to rely on manual connections for 100 times. You only need to simulate 100 connections in the unit test. And check whether the result is correct.

What should be tested

In the project, we have a large number of classes, it is unrealistic to cover all unit tests, we need to choose. Here are some of the factors I listed.

1. Data related

For example, in the local data storage module, we need to save different data. At this time, we can construct different test data through unit tests to save, and check whether the saving is successful. The data part is the part that needs to be covered most by the unit test.

2. Logic related

For example, in the connection module, some request results need to be filtered. This is a logic. For this logic, you can test whether the filtering is successful in the unit test, while manual testing does not need to pay attention to the filtering logic, only the filtered Whether the interface is displayed normally.

3. Multi-state module

For example, in the connection module, there are 8 connection states, including connectivity check, connecting, connected, etc. These states can be well simulated by unit testing, which solves the difficulty of simulating different states under manual testing. Conversion problem.

Six, summary

Our ultimate goal of writing code is only two: to meet the requirements and improve the quality of the code. Under the premise of ensuring the completion of the requirements, adding unit tests can improve the quality and maintainability of the code. Even after the introduction of unit tests, we may face Increasing the amount of code for research and development, spending more energy on writing unit tests, increasing development costs, but I think these can be overcome compared to the advantages brought by unit testing.
The above content is the entire content of this article. The above content hopes to be helpful to you. Friends who have been helped are welcome to like and comment.
If you exchange experience in software testing, interface testing, automated testing, and interviews. If you are interested, please follow me. We will have technical exchanges with colleagues.

Guess you like

Origin blog.csdn.net/Chaqian/article/details/107082429
Recommended