I am using KIF to test our iOS app. I am trying to make some tests that will go before and after my whole test sweet. I made a SenTestSuite category and overrode -setUp and -tearDown:
-(void)setUp
{
[tester loginCurrentVersion];
NSLog(#"setup");
}
-(void)tearDown
{
[tester logoutFromAnywhereIfNeeded];
NSLog(#"teardown");
}
These methods do get called, but my problem is that they both get called twice. I can't access any of the SenTestSuite.m methods. I am unsure why they are getting called twice. Why is it doing this and how can I solve this?
Thanks!!
Using a category to override a class's methods is really, really iffy. Instead, subclass SenTestCase and put your -setUp and -tearDown there. Then have your test classes inherit from it.
Since you are using KIF, your setUp and tearDown methods should be the beforeAll and afterAll. I also suggest you to take a look at the sample application and try to understand those tests.
Related
I'm going through a great book learning about test-driven development in Swift. My ultimate goal is to get a better understanding of OOP architecture. As I'm reading the book, a section early on states that the setUp() method is fired before each test method which I understand does the setup of objects to run the test for a pass or fail result. What I'm unsure of is how is this even possible I'm guessing from an architecture stand-point? How was Apple able to make a class that has a method which is fired before every other method in the class?
Here is some sample code:
import XCTest
#testable import FirstDemo
class FirstDemoTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
I think XCTest class has a lifecycle just like UIViewController for example.
Same as viewDidLoad() called after init(coder:) when the view is loaded into memory, setUp() method is also called after the test class is loaded (once during the life of the object).
You can also investigate native and third party test frameworks source code on github:
https://github.com/apple/swift-corelibs-xctest
https://github.com/google/googletest
You are Subclassing a Class called XCTestCase. Usally Testframework introspects Classes in which Test-Methods are defined and run them in a particular order.
Anyway, im not 100% sure if the XCTest is working this way, but you could try to have a look at the source code and try to dig deeper there:
https://github.com/apple/swift-corelibs-xctest
For each test to be tested: setup() is called, then the actual test, then teardown().
Then again setup another test from that class and tearDown again...until all** tests of your test class get ran.
The sequence of the lines won't affect which test gets called first.
The reason that it's made this way is because 2 tests should be 100% independent. You don't want testExample do something to the object/sut/system_under_test (mutate its state) you're performing your tests on but, not undo what it's done. Otherwise your testPerformanceExample would be loaded from a state you weren't
expecting.
I have a UIViewController that contains each of UITextField, UIButton and UILabel. I put something in the UITextField, press a button and the string is now capitalized in the UILabel.
My question is: how do I set up the IBAction in Swift for unit tests? If there is no way to test the action in Swift, what else can I do to test this?
In the unit-testing world, the most difficult work could be UI testing. So, what you can do is to check whatever is available to you from the API.
You can not toggle an action or event like you are a phone user. So, you have to programmatically toggle actions or events in order to test on that. You will also have to programmatically initialize the UI elements yourself.
IBAction is just nothing but an indicator to tell UIStoryboard that this is a connector method, you can ignore and treat it as a normal method.
Unit Tests are not designed to test visual interface. It's made to test your code.
For testing the interface, you can use Apple's UI Automation tool or other external tools.
Personally I tried to do a mock for the class and send the following code:
controllerMock.btnNoResults.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
The problem is that the call is not being made to the mock so the test is useless.
The same method executed in Objective-C with OCMock works perfectly so what I do is I mix swift tests and Objective-C tests.
In order to setup the Objective-C test to test swift source code I need to basically do the following:
Add OCMock to my test project
Create an Objective-C test. It will ask me for the bridging header as usual
Import "OCMock.h"
VERY IMPORTANT: Import the header to access the swift functions on my project. This header is normally "ProjectName_Tests-Swift.h"
Use the following code:
-(void) testSeeMoreInformationAction{
id mock = [OCMockObject partialMockForObject:pController];
[[mock expect]seeMoreInformation];
[pController.btnSeeMoreDetails sendActionsForControlEvents:UIControlEventTouchUpInside];
[mock verify];
}
Like this my test works. Also the good thing of mixing Objective-C with Swift and adding OCMock is that you now can test many other things in an easier way. Please note that when you use OCMock and Swift it only works with iOS SDK functions, it will not work well with you swift functions.
Here is an example of another OCMock test very useful with AlertView:
- (void)testLoginShowsAlertViewWhenNoUserNameAndPassword{
pController.txtUserName.text = #"";
pController.txtPassword.text = #"";
id mock = [OCMockObject partialMockForObject:pController];
[[mock expect]presentViewController:OCMOCK_ANY animated:YES completion:nil];
[pController loginAction];
[mock verify];
}
Please feel free to contact me if you need further assistance with Unit Testing. I think is one of the most interesting parts of swift development.
how to test viewController, viewdidload & viewdidAppear, in test case it will never invoke. how to test my custom viewdidload code?
Are all test cases concurrent? or which test case will be invoke first in all test file. I want to fetch some data from network before all test case, how to implement it?
For 1. You can create UIAutomation bot for testing your code. If you don't want any integration tests, you can try calling directly following functions like [viewController viewDidLoad]
For 2. tests in one file are executed alphabetically, I saw a lot of people to do test like 001_testSomething, 002_testSomethingElse etc.
If you want to prepare your data you can do it in setUp function:
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
I have searched for an answer to this and cannot find one.
How would I call an instance method in the same class I am hooking in my Theos tweak?
If I was using standard Xcode i would use the self method i.e.-
[self method:arg];
But in a theos tweak this says cannot find the method, even if i hook that method.
Example:
%hook classimhooking
-(void)methodimhooking
{
[classimhooking methodiwanttocall];
[self methodiwanttocall];
%orig;
}
-(void)methodiwanttocall
{
%orig;
}
%end
The methodiwanttocall is there and i can hook to it, just not call it.
I have tried adding a new method with %new and calling that but it is not found, i have tried the same with a delay, but it is not found.
I have tried defining a variable of my class and calling that but it doesn't work.
I can also see that you can grab iVars directly, but this doesn't work with methods.
Any ideas would be appreciated.
If you want to call a method on the object you just hooked, you can use performSelector (or performSelector:withObject: if it has an argument), which should be something like that:
[self performSelector:#selector(methodiwanttocall)];
Sorry to dig an old thread. I may have an idea about the issue here. In case anyone with the same issue comes across this.
The problem is that the compiler does not know there is such a method, even there is. The way to deal with it is to import the header at the top of the code.
#import <classimhooking>
I am going through an application and adding Unit Tests. The application is written using storyboards and supports iOS 6.1 and above.
I have been able to test all the usual return methods with no problem. However I am currently stumped with a certain test I want to perform:
Essentially I have a method, lets call it doLogin:
- (IBAction)doLogin:(UIButton *)sender {
// Some logic here
if ( //certain criteria to meet) {
variable = x; // important variable set here
[self performSegueWithIdentifier:#"memorableWord" sender:sender];
} else {
// handler error here
}
So I want to test that either the segue is called and that the variable is set, or that the MemorableWord view controller is loaded and the variables in there are correct. The variable set here in the doLogin method is passed through to the memorableWord segues' destination view controller in the prepareForSegue method.
I have OCMock set up and working, and I am also using XCTest as my unit testing framework. Has anyone been able to product a unit test to cover such a situation??
It seems that Google and SO are pretty bare in regards to information around this area.. lots of examples on simple basic tests that are pretty irrelevant to the more complex reality of iOS testing.
You're on the right track, your test wants to check that:
When the login button is tapped doLogin is called with the loginButton as the sender
If some criteria is YES, call performSegue
So you should actually trigger the full flow from login button down to performSegue:
- (void)testLogin {
LoginViewController *loginViewController = ...;
id loginMock = [OCMockObject partialMockForObject:loginViewController];
//here the expect call has the advantage of swallowing performSegueWithIdentifier, you can use forwardToRealObject to get it to go all the way through if necessary
[[loginMock expect] performSegueWithIdentifier:#"memorableWord" sender:loginViewController.loginButton];
//you also expect this action to be called
[[loginMock expect] doLogin:loginViewController.loginButton];
//mocking out the criteria to get through the if statement can happen on the partial mock as well
BOOL doSegue = YES;
[[[loginMock expect] andReturnValue:OCMOCK_VALUE(doSegue)] criteria];
[loginViewController.loginButton sendActionsForControlEvents:UIControlEventTouchUpInside];
[loginMock verify]; [loginMock stopMocking];
}
You'll need to implement a property for "criteria" so that there is a getter you can mock using 'expect'.
Its important to realize that 'expect' will only mock out 1 call to the getter, subsequent calls will fail with "Unexpected method invoked...". You can use 'stub' to mock it out for all calls but this means it will always return the same value.
IMHO this seems to be a testing scenario which has not properly been setup.
With unit tests you should only test units (e.g. single methods) of your application. Those units should be independent from all other parts of your application. This will guarantee you that a single function is properly tested without any side effects.
BTW: OCMock is great tool to "mock out" all parts you do not want to test and therefore create side effects.
In general your test seems to be more like an integration test
IT is the phase of software testing, in which individual software modules are combined and tested as a group.
So what would I do in your case:
I would either define an integration test, where I would properly test all parts of my view and therefore indirectly test my view controllers. Have a look at a good testing framework for this kind of scenario - KIF
Or I would perform single unit tests on the methods 'doLogin' as well as the method for calculating the criteria within your if statement. All dependencies should be mocked out which means within your doLogin test, you should even mock the criteria method...
So the only way I can see for me to unit test this is using partial mocks:
- (void)testExample
{
id loginMock = [OCMockObject partialMockForObject:self.controller];
[[loginMock expect] performSegueWithIdentifier:#"memorableWord" sender:[OCMArg any]];
[loginMock performSelectorOnMainThread:#selector(loginButton:) withObject:self.controller.loginButton waitUntilDone:YES];
[loginMock verify];
}
Of course this is only an example of the test and isn't actually the test I am performing, but hopefully demonstrates the way in which I am having to test this method in my view controller. As you can see, if the performSegueWithIdentifier is not called, the verify with cause the test to fail.
Give OCMock a read, I have just bought a book from amazon about Unit Testing iOS and its really good to read. Looking to get a TDD book too.