How to Setup Unit Tests for cocos2d apps? - ios

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();
}

Related

iOS testing with XCTest and separate file

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().

Know at runtime that you are running in IBDesignable

(programming in swift 2)
In my iOS application I am using frameworks. I am also using the IBDesignable feature with great success. I have a bunch of views and buttons in frameworks that are IBDesignable and thus render nicely on the screen of interface builder.
Problem is that one of these views executes code when its initialized that will do an assertion if its being run just in the context of the IBDesignable interfacebuilder (executed just to render the IB screen). The reason for the assertion is valid, and really does not matter, bottom line is that I do not want to loose this functionality during "normal" operation.
My idea for a solution is to know when the code is being executed just for rendering in IB for IBDesignable mode, and thus build in a switch to not/do the assert.
I tried using the #if !TARGET_INTERFACE_BUILDER but this is not working, as this is a preprocessor directive and thus only evaluated a compile time, and the frameworks are precompiled (when used in the actual app).
I also thought about using prepareForInterfaceBuilder() but this is not applicable because the class throwing the assert has nothing todo with UIView itself.
Is there a function or any other way to check AT RUNTIME (in a framework) that your code is being run as part of IB screen rendering for IBDesignable mode?
After testing a few dozens solutions, I found the following to work reliably:
/**
Returns true if the code is being executed as part of the Interface Builder IBDesignable rendering,
so not for testing the app but just for rendering the controls in the IB window. You can use this
to do special processing in this case.
*/
public func runningInInterfaceBuilder() -> Bool {
//get the mainbundles id
guard let bundleId = NSBundle.mainBundle().bundleIdentifier else {
//if we don't have a bundle id we are assuming we are running under IBDesignable mode
return true
}
//when running under xCode/IBDesignable the bundle is something like
//com.apple.InterfaceBuilder.IBCocoaTouchPlugin.IBCocoaTouchTool
//so we check for the com.apple. to see if we are running in IBDesignable rendering mode
//if you are making an app for apple itself this would not work, but we can live with that :)
return bundleId.rangeOfString("com.apple.") != nil
}
SWIFT 4 version
/**
Returns true if the code is being executed as part of the Interface Builder IBDesignable rendering,
so not for testing the app but just for rendering the controls in the IB window. You can use this
to do special processing in this case.
*/
public func runningInInterfaceBuilder() -> Bool {
//get the mainbundles id
guard let bundleId = Bundle.main.bundleIdentifier else {
//if we don't have a bundle id we are assuming we are running under IBDesignable mode
return true
}
//when running under xCode/IBDesignable the bundle is something like
//com.apple.InterfaceBuilder.IBCocoaTouchPlugin.IBCocoaTouchTool
//so we check for the com.apple. to see if we are running in IBDesignable rendering mode
//if you are making an app for apple itself this would not work, but we can live with that :)
return bundleId.contains("com.apple.")
}

Can I share codebase between OpenGL ES2- and OpenGL ES3-based renderers?

In the app I'm writing there is a class cluster that provides rendering means. It resolves to OpenGL ES3 version when the device is capable of that, and falls back to OpenGL ES2 version otherwise. It looks roughly like the following code.
/* Client code */
/* `Renderer` is the class cluster instantiated like this: */
self.renderer = [Renderer rendererForAPI:self.context.API];
/* ----------- */
/* Inside `Renderer` implementation block: */
+ (instancetype)rendererForAPI:(uint)apiVersion
{
Renderer *renderer;
if (apiVersion >= 3) {
renderer = [[RendererForES3 alloc] init];
} else {
renderer = [[RendererForES2 alloc] init];
}
return renderer;
}
/* --------------- */
/*
* Both `RendererForES3` and `RendererForES2` classes
* are descendants of `Renderer`.
* One of them imports `OpenGLES.ES3`,
* and the other imports `OpenGLES.ES2`.
*/
I do not want the code to duplicate overly. Some of my drawing methods are very similar in both members of the cluster, and some of the methods are nearly identical.
I realise that OpenGL calls, even with exactly the same name, may map to different offsets in different versions of the standard.
So the question is how to keep code duplication at its minimum.
There are two obvious solutions that sprung to my head.
The first one is to inherit ES3 Renderer from ES2 Renderer, overriding necessary methods and keeping the rest. But in that case, ES3 Renderer would receive both OpenGL imports at the same time. I'm not sure which precautions are to consider in that case, and how well will it work on different hardware.
The second solution is simply to keep the repeating code in a text file that would be included in both implementations via #include directive. But this seems to encumber maintenance.
Perhaps there are some advice on how to reuse code in renderers which rely on different versions of OpenGL standard. Maybe it's an architecture issue and my code should have been designed some other way.
What decision should I take?
For now, I chose to subclass my ES3 renderer from the ES2 renderer.
It seems to work fine, as ES3 is emphasised as a superset of ES2.
The most noticeable drawback is that version 100 shaders and version 300 shaders can not be linked together. That means if one intends to support both ES3 and ES2, it might be necessary to have two separate shader codebases.
Also worth keeping in mind:
https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/AdoptingOpenGLES3/AdoptingOpenGLES3.html

Run unittests with a specific locale (en_US or none)

Is there a way to force a specific locale when running unittests?
Eg. always use en_US or force no locale, so that none of the .lproj files are loaded.
My unittest is sensitive to the currently selected language in the Settings app (in the iOS Simulator). Preferably I would like my unittests not to be language sensitive.
Below is my unittest code that shows the problem
#interface MYGalleryTests : SenTestCase
#end
#implementation MYGalleryTests
-(void)test0 {
// This test doesn't work.
// I get 'No pictures' when locale is en_US
// I get 'Ingen billeder' when locale is da_DK
NSString* actual = [MYGallery emptyMessage];
NSString* expected = #"EMPTY_MESSAGE";
STAssertEqualObjects(actual, expected, nil);
}
#end
#interface MYGallery : NSObject
+(NSString*)emptyMessage;
#end
#implementation MYGallery
+(NSString*)emptyMessage {
return NSLocalizedStringFromTable(#"EMPTY_MESSAGE", #"Gallery", #"Message shown when gallery is empty");
}
#end
en.lproj/Gallery.strings
/* Message shown when gallery is empty */
"EMPTY_MESSAGE" = "No pictures";
da.lproj/Gallery.strings
/* Message shown when gallery is empty */
"EMPTY_MESSAGE" = "Ingen billeder";
There is a way to run the unit tests in a specific locale, but it isn't good for unit testing, but it can be useful for testing.
In Xcode, go to Product -> Scheme -> Edit Scheme… -> select Test -> Options and then set the 'Application Language' popup to the desired language. Then your unit tests will run in the specified language. You can use this to test code for a specific locale. You can use this to get faster test turnaround than actually running your app, especially if you only run a specific subset of unit tests. However, it is not useful for unit testing in general because you're limited to the specified language.
Also, doing this will modify your .xcscheme, so you'll need to change that back when you're done.
One way to change the locale for unit testing is to use the technique specified here. However, that technique uses method swizzling, which doesn't work with Swift.
Just specify the lang in run options.
Posting a proper answer so you can mark this as answered.
How to force NSLocalizedString to use a specific language has a solution for this problem.

Check if the running code is unit test case or not

I want to check if the running code is unit tese case or not execute different code for the result such as:
if ( unit test case is running )
{
do something
}
else
{
do other thing
}
any idea on this?
This is a bad approach you should try to simulate logic parts which you are trying to avoid by this statemetn through Mock ojects or other mechanism.
Now to your question you can use a oolean variable like isUnittest which you set on test setup and Teardown, ut as saied i dont recommend you doing this.
This seems to work for me (iOS 8, Xcode 6):
- (BOOL) isRunningTest {
return NSClassFromString(#"XCTestCase") != nil;
}
I think this is cleaner and easier than other answers.
Another way is to let the class have customizable behavior controlled via a static method, and have test case call that method in its static load method.
I had a similar issue using storyboard and an external restful service for authentication via oauth. The app delegate would check if there is a valid oauth token in appdelegate:didFinishLaunchingWithOptions, and if not, then programatically trigger segue to do the oauth login. But this was not desirable in test cases. To solve this, I created a static method within the app delegate to disable the login screen. This is the code within my app delegate:
static Boolean showLoginScreen = TRUE ;
+ (void) disableLoginScreen
{
showLoginScreen = FALSE ;
NSLog(#"disabled login screen") ;
}
The test case had its load method to do the following:
// disable login screen for the test case
+ (void) load {
NSLog( #"now disabling login screen" ) ;
[XYZAppDelegate disableLoginScreen];
}
This worked because the test case class was loaded before application was initialized. Of course you must check the value of this flag within app delegate to trigger/not trigger the login segue. Other alternatives I tried but rejected were as follows:
Create a preprocessor define on the test target. But the compiler only compiles the test case files with this flag, not the application source. See http://www.innovaptor.com/blog/2013/09/02/xcode-preprocessor-macros-for-test-code.html
Use the static initialize method of the test case to call the disable method. On test case run, the application is started before the test case class is loaded. See http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/ for some details.
Don't message UIAlertView directly. Instead, use dependency injection, for example, a property like
#property (strong, nonatomic) Class alertViewClass;
Then your code to create an alert can do
UIAlertView *alert = [[_alertViewClass alloc] initWithTitle:…etc…];
In your test code, inject a different class. I use https://github.com/jonreid/JMRTestTools to specify JMRMockAlertView. I can then test the alert calls with JMRMockAlertViewVerifier. (In fact, this enables test-driven development of alerts.)
Edit: These days, I use https://github.com/jonreid/ViewControllerPresentationSpy

Resources