Before you begin
This article focuses on how to use unit tests during the development of an iOS program. Use the ocunit that comes with Xcode as the test framework.
I. Overview of UNIT Testing
Unit testing, as one of the components of Agile development practice, aims to improve the efficiency of software development and maintain the health of code. The goal is to prove that the software works correctly, rather than finding bugs (the goal of discovering bugs is positively correlated with development costs, although finding bugs is a means of guaranteeing software quality, which is clearly the opposite of reducing the cost of software development). It is a guarantee of the quality of software, for example, after refactoring we need to ensure the normal operation of software products.
Many people think that writing unit tests is not a guarantee that unit tests are not guaranteed to reduce the likelihood of bugs occurring, and because writing unit tests can take some time and effort, and thus inevitably increases costs. Objectively speaking, this is largely due to the fact that the level of the programmer is not high enough. I think the necessary conditions for using unit tests to bring great benefits are as follows:
- Programmer's own level of programming-whether there is more code experience, proficiency in refactoring
- Programmer's knowledge of the project-whether the software or module needs to be understood correctly
- Project Quality-whether stable, long-term multi-version, need to deal with more changes
If the programmer has a high level of understanding of the requirements and the project needs to face a lot of changes, then it is no doubt that unit testing is very useful for software. If the software function is simple and the development cycle is short, it does not need to carry out complicated maintenance work, then the unit test is not very meaningful.
The benefits of excellent unit testing practices:
- A good unit test is a good document and is more acceptable to the programmer than the document, which directly describes the testers ' expectations of the results of the code under test.
- When the code is maintained by someone else (or when you refactor it yourself), the code is guaranteed to be correct when adding new features or modifying old functionality by constraining the unit tests.
- The automated execution of unit tests ensures that the code will be tested throughout the development process, which is very much in line with XP thinking.
- In the face of changes in software functionality, programmers can be more comfortable with code refactoring without worrying about breaking the original functionality.
- Good unit tests can reduce the number of bugs, and for project management, the process of modifying bugs cannot be planned, making the software development process easier to control.
- A test that describes a class's behavior can be written by an old programmer to guide the new programmer in encoding the class.
- ......
There are many benefits, but the most important thing is to ensure the quality of software at the same time , due to reduce the bug and response to change caused by the return of the bug, and improve labor productivity . Moreover, in the agile process, the use of unit testing is a must-have means, otherwise there is no guarantee of the correctness of the refactoring, resulting in code can not face changes.
II. Overview of unit Testing for iOS
When I first approached client programming, I couldn't figure out how to write unit tests for a client program for a long time. Unit testing is essentially a way of judging results with assertions, and how is this applied to interface tests with complex interactions?
All we have to do is translate the client code into code that is easy to test. What kind of code is easy to test? It's at least like this:
1, the measured method needs to produce measurable results.
2, the relationship between the classes should be loosely coupled.
The first of these is the necessary condition. Using assertions in this form indicates that the method of testing ultimately results in some measurable result. As a result, we need to isolate the presentation and business logic as much as possible. The code shown is impossible to test, for example, some methods just play the animation. And the business logic will eventually result in some data changes, which is easy to test.
Roughly speaking, as an iOS programmer, the first thing to know is a pattern called MVC. This pattern defines the overall structure of the cocoa touch framework. In the iOS program, we also need to follow this pattern to write the interface code. The Designed class has a good structure and is more suitable for unit testing.
Then be sure to refactor the code so that we can keep the code constantly improving and becoming more suitable for unit testing.
There are frameworks that can help you better test the Ocunit, GTM, Ghunit, CATCH, Ocmock, but for me, Ocunit is enough. As Apple's official testing framework, its greatest advantage is its ease of use.
Three, Unit test practice
Here are some of the better practices in unit tests that I understand.
As the name implies, unit tests object to a unit, which derives from the term "compilation unit" of the compiler domain. In a process-oriented way, a function is referred to, and in an object-oriented way, it is usually "class". Therefore, each functional class should provide the corresponding unit test.
Practice 1 each feature class should provide unit testing, and each test class depends only on the class under test that it is testing. Use of forged objects avoids dependencies on other classes.
The explanation guarantees that a test class is focused on only one of the tested classes, and when the test fails, it can quickly locate who has made the mistake without being disturbed by other classes.
Simple data classes can not be provided, but to ensure that the test is covered. There is no appropriate metric to quantify the success of a unit test scenario. Common standards (Code coverage and the number of test cases executed successfully) can be artificially modified (falsified) in the same quality as the software under test. Of course, in the absence of a programmer's quality, the use of such standards is also possible (or, to be sure, necessary) as a means of failure. Unit tests require the programmer to check what features do require testing coverage. That's why some programmers don't believe that unit testing can increase productivity--it's more dependent on the quality of the programmer, which is not guaranteed. But the same, because agile is a people-oriented thinking practice, so this behavior seems to be a necessity.
Practice 1.1 Use forged classes to avoid dependency on other classes.
Explain a means of avoiding dependency.
For example, one of the method declarations that is being measured is this:
-(void) xxxx: (person *) person;
If the person is passed into the test, it causes the test class to depend on two classes. When a test fails due to an error in person, it is not possible to quickly navigate to a problem in the class being tested. In this case, you can use a forged class. If only one attribute of person is used in the method name, then the method name can be refactored to
-(void) XXXX: (ID) person; (here ID is debatable, this is the simplest)
You then add a Fakeperson that contains only the name attribute to the target of the unit test as a forgery class. This makes it possible to quickly speculate on the source of the error once an error occurs.
Practice 1.2 Use a forged environment to avoid interference from other environments.
Explains the method tests that are appropriate for async.
One of the most frequently encountered scenarios is to test code that has a network environment. Because of the asynchronous presence, this can cause the test code to write poorly. A simple solution is that we assume that the network must be unobstructed, then we test the code will be divided into two parts, that is, the assembled send function and receive parsing function. If both the transmit and receive functions can pass the test, we can determine the correctness of the asynchronous method. Another approach is to use Ghunit, which supports testing of asynchronous code.
Practice 2 The test Case (method) name should be self-explanatory and independent.
Explain basic skills.
If the name of the class being tested is XXX, then the test class can be named Xxxtests. For the function to be tested, the naming should be self-explanatory. This can locate the problem as quickly as possible when an error is found. For example, if a property, obj, should be non-null, then we can name it:
-(void) testobjnotnil{}
Each method target should be single, and in most cases there is only one assertion statement within each method, and the method should not rely on the results of other methods as input, guaranteeing atomicity.
Practice 3 assertion statements need to interpret the intent of the tester.
Explain basic skills
Each unit test framework provides a lot of assertion statements, which are essentially the same. But testers need to choose the right statement for their own purposes, so that they can understand the purpose of use case design when they read the test code. For example, Stassertnil and Stassertnotnil, and so on.
Practice 4 A good way to judge whether an intention has been achieved is to determine whether the data affected by the detection method has a reasonable change.
Explain basic skills
Since unit tests are judged using assertion statements, the easiest thing to do is to judge the changes in the data. This limits the range of methods that a unit test can test, the method that causes the data to change . For some purely display methods, such as playing a special effect, this method cannot be constrained by unit testing. The characteristics of the test data include the range of values (int, float, etc.), the Order of arrangement (Nsarray, etc.), the type, and so on.
Practice 5 uses refactoring to make methods easier to test.
Interpreting unit tests is a means of securing refactoring, and refactoring can make code easy to test.
What kind of code is easy to unit test? The simplest point is that each test method should be functionally single . This is, of course, what should be done in the code specification. Method has a single function, the assertion of the test method is also better determined. If you find that a method is difficult to test, you should do a split refactoring of the method.
Practice 5.1 is for the relationship between abstract design classes.
Interpretation facilitates the realization of counterfeit classes.
Communication between classes if you rely on abstractions (interfaces), you can use forged classes more easily. Refer to Practice 1.1.
Practice 6 uses a top-down approach to building classes.
Explaining the top-down approach can make the class function clear, the composition of the class will be clear and compact, and there will be no waste methods.
Determine the public methods and properties that the class has by determining the responsibilities that the class needs to bear. Refactor the code in public methods into private methods to make the method as compact as possible.
Practice 6.1 should provide testing for all exposed properties and methods, while private methods do not.
Explain that if you use a top-down approach to building classes, private methods in theory should all be reconstructed from public methods. In fact, these private methods should be tested when testing public methods. Also, because private methods are more likely to change than public methods, it can cause unnecessary changes to the cost of testing the code.
The callback method is not a private method and needs to be tested.
The test method for practicing the 6.2 callback method is to call directly.
Explain basic skills
Because callback methods are generally asynchronous and non-triggering (as normal processes), such as the return of network events and the trigger event of a push button. Therefore, the test should be directly used to detect its flow. For example, a button's touch up inside event:
-(void) buttonpressed: (ID) sender;
You can test a Fakebutton button as a parameter pass based on the method used in the method and the property.
Practice 6.2 Testing private ways, KVC, subclasses and categories.
Explain basic skills.
Consider using KVC and subclasses when you encounter tests that need to be written by validating private data. Subclasses inherit from the tested class and are only included in the unit test target, which is to make the tested class have some ability to be easily measured if it should not be changed.
Practice 7: Changes require support for new tests.
Explanation: Guarantee the coverage of the test.
Just as the "change requires abstraction" mentioned in agile, changes in testing require new testing. Of course, the degree remains the programmer's own control.
Iv. General Process
The biggest benefit of using Ocunit is that the process is very simple and simple enough to make you feel very happy. With Xcode support, adding tests becomes surprisingly simple. Simply tick "Include Unit Tests" When you create a new project, and an example will be added automatically. Then, when you need to add a new unit test, create a new "objective-c Test Case Class".
In the test file, as long as you know where Setup is initialized, teardown is the place to end the cleanup, and they are re-executed when each use-case method executes-which guarantees the atomicity of the test case. Then you know that each test case is prefixed with test and has no return value. You can then write an assertion statement in the method. Enter STASSERTXXXXX to see their Lenovo hints. Once written, the menu product->test and the unit test is complete!
V. Test-driven (TDD)
The agile approach refers to the development of TDD. The main purpose of TDD is to give developers more confidence in the code they write, making it more practical for developers to modify the code. For its summary, it is more appropriate to quote the original text: "The beauty of test-driven development is that it takes demand as the lead, through the form of testing, to guide the developers to design and architecture of the software, and write the most refined code, so that test cases run through." After proper refactoring, test cases and product code can achieve a healthier state. As mentioned above, the class is designed by the top-down form, and the class is constantly examined and reconstructed through unit testing to achieve the health of the code.
If you write unit tests after the code is written, you don't have the benefit of this pattern. It's like writing the code and then filling in the document, it doesn't make any sense. Tests should be written and updated before the code starts, or in code writing, in order to keep the code progressing. And that's what TDD means.
Vi. Summary
The code for unit tests is so simple, but it's not a simple matter to write a good unit test. It requires a deeper foundation of the programmer. Because of the personal level, there are some things that are more verbose. Simplifying a complex problem is a skill and a long way to go. I hope you can use this simple and efficient technology in your daily development.
Finally, as a Chinese, blessing Yaan. Some people on the internet are still spraying on this kind of event. I want to say that, although the world is objective, but everyone's world view is indeed subjective. A person's view of the world determines what he thinks the world is like. The national character of the Chinese nation is restrained, in the external performance seems to be less than the western publicity. But like Wenchuan, the National people in this kind of timetable show the inner quality of our nation. Mouse excrement everywhere is mouse excrement, I am proud of being Chinese.
Original address: http://www.cnblogs.com/slegetank/archive/2013/04/20/3030875.html
Unit Tests for iOS development