4. Testing for failed Debugs
It's time to trace the failure of the previous test. Open GAMEBOARD.M, find Cellstateatcolumn:androw: And SetCellState:forColumn:andRow: Methods, you'll see that they all call a checkboundsforcolumn: Androw: The helper method used to detect array boundaries.
The method comments in the header file GameBoard.h are as follows:
Raises an nsrangeexception if the column or row is out of bounds
However, if the boundary is exceeded, Checkboundsforcolumn:androw: the implementation of the method throws a nsgenericexpression. Normally, the annotations in your head file are used as a public API specification, but in this case the code and specification do not match, what do you do?
One possibility is to update the annotations and related tests to match the current implementation. In this example, the annotations in the specification look more meaningful: a boundary check should follow Nsarray and raise a nsrangeexception.
In GAMEBOARD.M, update Checkboundsforcolumn:androw: The implementation of the method is as follows:
-(void) Checkboundsforcolumn: (nsinteger) column Androw: (nsinteger) row{if (column < 0 | | column > 7 | | Row < 0 | | r ow > 7) [nsexception raise:nsrangeexceptionformat:@ "row or column out of Bounds"];}
Rerun the test, at which point all tests should be able to pass.
Since the code is out of sync, the specification in the note is always a bit dangerous. However, your test can add double insurance to the comment. Since you've written your test code, the risk of these implementation mismatches will be greatly reduced as long as you run your tests frequently!
In addition, your tests provide a great high-level overview code, especially after following the recommended naming conventions. This is handy when you re-enter a code you haven't touched for a long time-as in the next section.
5. Guaranteed Test Bugs
A crash report has just entered your app: one of your testers reports that when she runs the game and clicks the screen directly (without clicking the "2 Player" or the "vs Computer" button), the program crashes.
If you try it yourself, you'll see the following information in the console:
REVERSIGAME[1842:A0B] * * * terminating app due to uncaught exception ' nsrangeexception ', Reason: ' Row or column out of Boun ds
Crashes seem to happen repeatedly, what is thrown out of a nsrangeexception? The call stack provides the following information:
2 corefoundation +[nsexception Raise:format:] + 1393 reversigame-[gameboard checkboundsforcolumn:androw:] + 142 4 Reversi Game-[gameboard Cellstateatcolumn:androw:] + 765 Reversigame-[reversiboard Flipopponentcountersforcolumn:androw: Withnavigationfunction:tostate:] + 2816 reversigame-[reversiboard makemovetocolumn:androw:] + 245 7 ReversiGame-[ Boardsquare celltapped:] + 192
Read from bottom to top:
7th, 6 lines of TAP trigger code to handle the player's movements
The 5th line of the game logic checks if any opponent's pieces are surrounded and flipped
The 4th, 3 lines of code then call Cellstateatcolumn:androw: and
Checkboundsforcolumn:androw:
The bottom frame of line 2nd reports an out-of-bounds exception
Want to learn more about debugging crashes? Look here
My App Crashed, now what?http://www.raywenderlich.com/10209/my-app-crashed-now-what-part-1demystifying IOS Application Crash Logshttp://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs
This is a great opportunity to test these crash conditions.
Your new test not only fixes the problem, but also ensures that the bug remains fixed as a regression test. There's nothing more annoying than fixing a bug, adding new features or refactoring code a few months later, and then having the same bug.
6. Decide what needs to be tested
You know you need to test--but what should you test?
Reversiboard is a generic implementation of the GameBoard class, so it makes sense to start troubleshooting here.
Use the Ios\cocoa touch\objective-c test Case Class template to create a new class named Reversiboardtests, inherited from Xctestcase.
Before you begin, delete the Testexample method of the template file, and then import the header file in reversiboardstests.m :
#import "ReversiBoard.h"
Change @interface in reversiboardstests.m as follows:
@interface reversiboardtests:xctestcase {reversiboard *_reversiboard;}
Adding a _reversiboard instance variable means that you do not have to instantiate it repeatedly in each test method.
Then modify the Setup method as follows:
-(void) setUp {[Super Setup];_reversiboard = [[Reversiboard alloc] init];}
7. Test negation
In the previously written test, the presence of the exception was the expected result. This time, the exception did not appear on your test basis.
Add these methods to REVERSIBOARDSTESTS.M
?-(void) Test_makemove_inpregamestate_nothinghappens {[_reversiboard settopregamestate]; Xctassertnothrowspecificnamed ([_reversiboard makemovetocolumn:3 androw:3],nsexception,nsrangeexception,@ "Making A Move in the pre-game state should does nothing ");}
In the above code, the test sets the state before the game. In other words, the player will play against AI or play against other players before selecting the status. This test simulates the action of clicking on a chessboard when entering a game.
Xctassertnothrowspecificnamed assertions are just the opposite of xctassertthrowsspecificnamed. If the specified exception is thrown, the test above fails, and if the specified exception is not thrown, the test passes.
You haven't fixed the bug, so running the code now will fail--but it's a good thing to write a test before fixing a bug means you have the ability to test to reproduce the bug.
command+u Run the test, you will see the following information:
Test failure:-[reversiboardtests Test_makemove_inpregamestate_nothinghappens] Failed: (([_reversiboard Makemovetocolumn:3 Androw:3]) does not throw <nsexception, "nsrangeexception" >) failed
8. Correction Code
Open reversiboard.m and find Makemovetocolumn:androw: method.
Think about how to fix this particular bug. It makes sense that only the user chooses the game mode to move. Such a thought, the game before and after the game logic is no different.
Fortunately, there is a property that specifies the current game state: GameState.
Add the following code to the Makemovetocolumn:androw: Top of the method:
if ([self gamestate]! = Gamestateon) return;
This condition detects the current state of the game. If the status is not gamestateon--the game is not running-the method terminates immediately.
Run the app and test if clicking the board before selecting the game mode crashes?
Finally,Command+u runs the test, and test navigator should show a green tick, and the bug is crushed!
Exploratory style testing uses only code that contains obvious problems, but regression style testing can provide a guarantee for fixing a problem frequently.
Fixing each bug not only makes your code healthier, but also gives you more time to think about your unit tests.
Third, where to go next?
Testing is a huge task for a development career, and we have mastered the basics of unit testing, and here are some useful concepts:
- Which assertion is used? Where to use assertions?
- Add tests to an existing app
- Use the test in the program description
- Explore and fix bugs
- Make sure that a bug that has been fixed no longer appears
The integrated Xctest in Xcode makes it easy to build test suites, with a wide range of testing concepts throughout the iOS field:
- Mock objects
- Simulate enough real objects in the test.
- UI Testing
- You can simulate a user's input, such as touch or text input.
- Continuous integration (CI) systems
- Unit tests will be run automatically to learn more about CI features, read book 14, chapter 15 "Beginning and intermediate continuous integration in Xcode 5
Before the deep learning test, there are a few challenges to get you through the concept of this chapter.
Iv. challenges
The GameBoard class still has some methods that haven't been tested-your task is to write tests and provide a complete test suite for your app.
1. Challenge One: Test Clearboard
Clearboard clears all the pieces on the board. Since the getter and setter methods have been tested, you can assume that these methods do not need to be tested again.
The Celarboard test case has the following steps:
1) set at least one black chess on the board
2) set at least one white on the board
3) Call Clearboard
4) Check that the place where you place white and black is empty now (hint: the status is Boardcellstateempty)
Remember the naming format that test cases follow: unit or method name, test what, expected result
2. Challenge II: Test scorekeeper
Countcellswithstate: Records the number of specific pieces of chess on the board. This method calculates the final score, so it is necessary to make sure it works correctly!
Countcellswithstate: The test case will perform the following actions:
1) Set some black or white chess on the board
2) track the number of pieces added
3) Compare your number with countcellswithstate: number of returns
Countcellswithstate: There is a status parameter, so it looks like this
[_board Countcellswithstate:boardcellstatewhitepiece]
Again, make sure your test case is properly named
I wish you a successful test!
Appendix: Xctest Assertion Reference
All of the following assertions are used (format ...) As the last parameter, this nslog-style parameter displays a message when the test fails.
Xctfail (Format ...) unconditional failure; Xctassertnil (exp, format ...) used to mark portions of code that should not be executed XCTASSERTNOTNIL (exp, format ...) The expression should be nil or not nil, use Xctassert (exp, format ...) in the OC object XCTASSERTTRUE (exp, format ...) XCTASSERTFALSE (exp, format ...) The expression should be true or False Xctassertequalobjects (A1, A2, Format ...) OC Objects A1 and A2 should be equal; use isequal: To maintain Equality xctassertequal (A1, A2, Format ...) Parameters A1 and A2 should be equal; used to compare C quantities, sets and structures (e.g. CGRect and cgpoint), Nsvalue to compare Xctassertequalwithaccuracy (A1, A2, Delta, Format ...) The parameter A1 and the parameter A2 should be equal to the given delta value; using the float and double types, where the decimal value may not be accurate xctassertthrows (exp, format ...) XCTASSERTTHROWSSPECIFIC (exp, exception, format ...) Xctassertthrowsspecificnamed (Exp,exception,exceptionname,format ...) The expression should throw an exception message, and a more detailed version lets you point out the class name and the exception name xctassertnothrowxctassertnothrowspecificxctassertnothrowspecificnamed if the exception is thrown, These assertions will fail
IOS7 Unit Test