Currently, I write my testing in 1 file (default). Is it possible to have multiple files and test?
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, #"Pass");
}
You can have many files and many tests per file. The first one is just the one made by Xcode automatically, but all that makes it special is that it:
Implements a subclass of XCTestCase
Is a member of the testing target (not your app target)
Making another file with the same characteristics is simple.
In Xcode, File -> New -> iOS -> Source -> UI Test Case Class
The convention is to group related tests into a source file, and then name that source file ___Tests.swift (or .m)
For example, SharingTests.swift might contain the methods testSharingToFacebook() and testSharingToTwitter().
Related
My XCode version is 7.3.1 .
My project already has test project, and there are already some test case classes. I can see them when select the "Test Navigator" tab which is one of this on left top of XCode.
Now, I want to create a new test case, so I selected the new Unit Test Case Class:
And I add very simple code into it:
#import <XCTest/XCTest.h>
#interface MyTest : XCTestCase
#end
#implementation MyTest
- (void)setUp {
[super setUp];
}
- (void)tearDown {
[super tearDown];
}
- (void)testExample {
NSString* expected = #"my-test-string";
XCTAssertEqual(expected, #"my-test-string");
}
#end
I need to run it, so I go to "Test Navigator" again, but I can't see this test case class I just created. Why?
Restarting Xcode is the only thing that worked for me in Xcode 9.0 beta 6.
It appears that Xcode adds the individual testmethods/classes only after the whole unit test target has been run at least once.
So select your test target, run all tests using Command u. After that you should see all tests in the test navigator and you can run them individually.
Sometimes the unit test appears in Test Navigator after you Save (CTRL+S) the unit test's file in XCode.
Remember to keep the test prefix on the test methods, otherwise they are not recognized by the test framework, e.g.:
func test_When_empty_Then_returnsNil() {
not
func when_empty_Then_returnsNil() {
I found out that Xcode 7 (Version 7.0 (7A220)) changed the order in which +load methods for classes and categories are called during unit tests.
If a category belonging to the test target implements a +load method, it is now called at the end, when instances of the class might've already been created and used.
I have an AppDelegate, which implements +load method. The AppDelegate.m file also contains AppDelegate (MainModule) category. Additionally, there is a unit test file LoadMethodTestTests.m, which contains another category – AppDelegate (UnitTest).
Both categories also implement +load method. The first category belongs to the main target, the second one – to the test target.
Code
I made a small test project to demonstrate the issue.
It is an empty default Xcode one view project with only two files changed.
AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
+(void)load {
NSLog(#"Class load");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSLog(#"didFinishLaunchingWithOptions");
return YES;
}
#end
#interface AppDelegate (MainModule)
#end
#implementation AppDelegate (MainModule)
+(void)load {
NSLog(#"Main Module +load");
}
#end
And a unit test file (LoadMethodTestTests.m):
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "AppDelegate.h"
#interface LoadMethodTestTests : XCTestCase
#end
#interface AppDelegate (UnitTest)
#end
#implementation AppDelegate (UnitTest)
+(void)load {
NSLog(#"Unit Test +load");
}
#end
#implementation LoadMethodTestTests
-(void)testEmptyTest {
XCTAssert(YES);
}
#end
Testing
I performed Unit Testing of this project (the code and the github link are below) on Xcode 6/7 and got the following +load calls order:
Xcode 6 (iOS 8.4 simulator):
Unit Test +load
Class load
Main Module +load
didFinishLaunchingWithOptions
Xcode 7 (iOS 9 simulator):
Class load
Main Module +load
didFinishLaunchingWithOptions
Unit Test +load
Xcode 7 (iOS 8.4 simulator):
Class load
Main Module +load
didFinishLaunchingWithOptions
Unit Test +load
Question
Xcode 7 runs the test target category +load method (Unit Test +load) in the end, after the AppDelegate has already been created.
Is it a correct behavior or is it a bug that should be sent to Apple?
May be it is not specified, so the compiler/runtime is free to rearrange calls?
I had a look at this SO question as well as on the +load description in the NSObject documentation but I didn't quite understand how the +load method is supposed to work when the category belongs to another target.
Or may be AppDelegate is some sort of a special case for some reason?
Why I'm asking this
Educational purposes.
I used to perform method swizzling in a category inside unit test target. Now, when the call order has changed, applicationDidFinishLaunchingWithOptions is performed before the swizzling takes place. There are other ways to do it, I believe, but it just seems counter-intuitive to me the way it works in Xcode 7. I thought that when a class is loaded into memory, +load of this class and +load methods of all its categories are supposed to be called before we can something with this class (like create an instance and call didFinishLaunching...).
TL,DR: It's xctest's fault, not objc's.
This is because of how the xctest executable (the one that actually runs the unit tests, located at $XCODE_DIR/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest loads its bundle.
Pre-Xcode 7, it loaded all referenced test bundles before running any tests. This can be seen (for those that care), by disassembling the binary for Xcode 6.4, the relevant section can be seen for the symbol -[XCTestTool runTestFromBundle:].
In the Xcode 7 version of xctest, you can see that it delays loading of testing bundles until the actual test is run by XCTestSuite, in the actual XCTest framework, which can be seen in the symbol __XCTestMain, which is only invoked AFTER the test's host application is set-up.
Because the order of these being invoked internally changed, the way that your test's +load methods are invoked is different. There were no changes made to the objective-c-runtime's internals.
If you want to fix this in your application, you can do a few things. First, you could manually load your bundle using +[NSBundle bundleWithPath:], and invoking -load on that.
You could also link your test target back to your test host application (I hope you're using a separate test host than your main application!), which would make it be automatically loaded when xctest loads the host application.
I would not consider it a bug, it's just an implementation detail of XCTest.
Source: Just spend the last 3 days disassembling xctest for a completely unrelated reason.
Xcode 7 has two different load orders in the iOS template project.
Unit Test Case. For Unit Test, the test bundle is injected into the running simulation after the application has launched through to the main screen. The default Unit Test execution sequence is like the following:
Application: AppDelegate initialize()
Application: AppDelegate init()
Application: AppDelegate application(…didFinishLaunchingWithOptions…)
Application: ViewController viewDidLoad()
Application: ViewController viewWillAppear()
Application: AppDelegate applicationDidBecomeActive(…)
Application: ViewController viewDidAppear()
Unit Test: setup()
Unit Test: testExample()
UI Test Case. For UI Test, a separate second process XCTRunner is set up which exercises the application under test. An argument can be passed from the test setUp() ...
class Launch_UITests: XCTestCase {
override func setUp() {
// … other code …
let app = XCUIApplication()
app.launchArguments = ["UI_TESTING_MODE"]
app.launch()
// … other code …
}
... to be received be the AppDelegate ...
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(… didFinishLaunchingWithOptions… ) -> Bool {
// … other code …
let args = NSProcessInfo.processInfo().arguments
if args.contains("UI_TESTING_MODE") {
print("FOUND: UI_TESTING_MODE")
}
// … other code …
The injection into vs the separate process can be observed by printing NSProcessInfo.processInfo().processIdentifier and NSProcessInfo.processInfo().processName in from both the test code and the application code.
I'm trying the XCTest framework
#implementation UnitTestPOCTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testAditionClass
{
Addition *addObj = [[Addition alloc]init];
XCTAssertNotNil(addObj,#"AddtionClassExists");
}
I wrote the testAditionClass test method inside my test class, but when i run the test methods its showing Testing.... in the status bar
its not even getting terminated.
If any one know the the solution please help me.
Thanks in advance
I resolved this problem by opening the simulator you will have the following options in menu
if you tap on reset content and setting.
Then try running unit test it works for me.
I have been trying to figure out how to setup OCUnit unit tests for my game that uses cocos2d. So I followed these instructions to set up the test target initially: http://blog.shalomfriss.com/?p=894
Unfortunately, the code below triggers a signal SIGABRT when I run the test through Product > Test in Xcode. The function where it breaks seems to be at [CCGLProgram compileShader:type:byteArray:]
#implementation GameTests
- (void)setUp
{
[super setUp];
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testExample
{
STAssertNotNil([InitialLayer scene], #"initial layer scene was null.");
}
#end
This is the scene method from InitialLayer:
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
InitialLayer*layer = [InitialLayernode];
[scene addChild: layer];
return scene;
}
Some other possibly relevant info:
target device: ipad 6.1
xcode version: 4.6.3
cocos2d version 2.1
Does anyone know how to fix this or is there a better way in general to setup the tests using OCUnit or any other testing framework?
You don't want to unit test anything cocos2d-related. Without properly setting up the cocos2d CCGLView, the Director, and so on (the code that goes in the app delegate) in the setUp method that won't work to begin with.
Even when you do set it up, you always will be testing cocos2d itself, which will make your tests less isolated and you're almost required to set up a fresh new scene for each test in order to test any code in subclassed nodes. Which means the time to write tests as well as how long each test runs quickly grows.
Further complicating matters is that cocos2d's updates are tied to the CADisplayLink updates, and I'm not even sure they get fired when running a unit test. Even when updates do fire, you can't test them because a unit test runs once and ends, it has no concept "runs over a certain period of time". So something like "move a node to x,y and check that it got there after 3 seconds" is inherently not testable with the way cocos2d and unit tests work. At least not without heavily modifying cocos2d itself.
If you consider that cocos2d represents the view of your app, the real question is: why would you even want to unit test anything view-related in the first place?
Instead what you can do is to design your classes completely independent of the view. For example you can create extra game logic classes that are added to a node's userObject property and then control the node - the actual testable game logic happens in the model classes. You can have additional controller classes which fire off things like running animations, or particle effects, and any other view-related stuff that doesn't need to be tested - because at worst it won't look right, but it won't affect the gameplay.
One of the key benefits of test-driven development is to make classes testable, and that means not making them depend on the view. So your first goal should actually be to make tests that run without cocos2d.
If you want to see how this can be done, check out OpenGW and specifically the class reference. Besides pushing a game object's (OGWEntity) position and rotation to the view delegate (OGWNode) there's absolutely no dependency on view code. The intermediary class that performs actions on the view based on the entity's state is OGWController.
All of this makes OpenGW a highly testable game simulation engine. In fact it is so decoupled from anything view-related that I'm in the process of making the same code run with both Sprite Kit and cocos2d-iphone.
I agree that a complete test for everything is a waste of time but it doesn't mean to avoid it completely. It's good to separate your logic classes and methods and test them using the unit test to make sure that they are working correctly using gTest with modification to cmake. I did unit testing for util and logic classes like this:
I added this to end of my CMAKE file
if(WINDOWS)
set(UNIT_TEST_SOLUTION_NAME runUnitTests)
FILE(GLOB_RECURSE USER_TEST "Classes/*.test.cpp")
list(APPEND GAME_TEST
${USER_TEST}
#${USER_HEADER}
#${USER_CPP}
Classes/utils/common_operators/CommonOperators.cpp
Classes/utils/common_operators/CommonOperators.h
)
#list(FILTER GAME_TEST EXCLUDE REGEX "Classes/scenes/.*")
#list(FILTER GAME_TEST EXCLUDE REGEX "AppDelegate.cpp")
#list(FILTER GAME_TEST EXCLUDE REGEX "AppDelegate.h")
#list(FILTER GAME_TEST EXCLUDE REGEX "Classes/text/persian_language_support/.*")
set(run_unit_test_file
${GAME_TEST}
proj.unit_test/main.cpp
)
#option(test "Build all tests." ON)
enable_testing()
find_package(GTest CONFIG REQUIRED)
add_executable(${UNIT_TEST_SOLUTION_NAME} ${run_unit_test_file})
target_link_libraries(runUnitTests GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main)
add_test(runUnitTests runUnitTests)
endif()
And example test file is :
#include "gtest/gtest.h"
#include "CommonOperators.h"
TEST(CommonOperators, split) {
auto case1Result = CommonOperators::split("mamad reza masood", ' ');
std::vector<std::string> case1ExpectedResult = { "mamad", "reza", "masood" };
EXPECT_EQ(case1Result, case1ExpectedResult);
}
Please note that I downloaded google test (gTest) using vckpg and then placed its folder in my root directory.
Another note: For running your test in windows you need to define new solution with name that is mentioned in your cmake test part.And also you need to create main file like following in a new directory
#include "gtest/gtest.h"
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
After completing my app I run a test from Xcode, Product -> Test, it says 'Build Succeeded' and the app pops up for a split second, but then it prompt me with this message:
I have searched a lot but couldn't find any solution that works in this case. I want to mention that I have also tried changing the name of the application from, CSQTCConference, to CSQTC Conference, not sure how relevant this is.
I am planning to release my application to app store today, but this issue is holding it back. It will be helpful if you can suggest any pointers to resolve this issue.
I just comment the following line:
//XCTFail(#"No implementation for \"%s\"", __PRETTY_FUNCTION__);
in:
#import <XCTest/XCTest.h>
#interface KinderAppTests : XCTestCase
#end
#implementation KinderAppTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
//XCTFail(#"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
#end
This solution worked for me, hope it helps :)
I'm not familiar with XCTest but I assume XCTFail will always fail, so no big surprise here.
You are running boilerplate example code, which created a test that always fails. If you haven't written your own tests what's the point of running them in the first place?
The real question is: Why is this happening?
Answer: You did a Command U, or selected test. This ran the default test, which always fails.
Correction: Comment out the fail line, and run it again.