I need to test an instance variable in Objective C.
I have this snippet in myViewController.m
#implementation myViewController
{
NSView *myView;
}
I need to add a test in myViewControllerTest.mm that tests a case when myView.window happens to be nil
-(void) myTest
{
// 1. assert condition 1 is true
// 2. mock myView.window and set it to nil OR set myView.window directly to nil
// 3. assert condition 1 is false
}
I'd like to know the best approach to achieve step 2 in the test. Approaches I tried:
Add an explicit setter - (void)setMyView:(NSView*)myView just for the unit test purpose and use that in myTest.
I have a feeling that this is not a good way.
make myView as a property and put it in myViewController.h so that I'll have a setter without explicitly defining one.
Unsuccessful attempt at mocking using OCMock. (need to explore further)
Please suggest what the best approach is and what are the pros/cons of them.
Thanks !
Edit : Answer I ended up using in my test file.
[myViewController setValue:nil forKeyPath:#"myViewControl.window"];
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/BasicPrinciples.html#//apple_ref/doc/uid/20002170-BAJEAIEE
You can add setter only for testing in your test file myViewControllerTest.mm
This has no effect to your production code.
like below using Extension
// Write this in test file.
#interface MyViewController(Test)
- (void)setMyView:(NSView *)myView;
#end
or just use valueForKey: to get private field.
-(void) myTest
{
id myViewController = ...;
NSView *myView = [myViewController valueForKey:#"myView"];
myView.window = nil;
// ...
}
Related
This question already has answers here:
Objective-C: Property / instance variable in category
(6 answers)
Closed 2 years ago.
I've got a form implementation in objective-c and I'd like to extend my widgets (NSButton, NSTextField, etc..) to contain additional string representing their unique identifier string to be used after submit event occur, which trigger generation of json contain all widget id/value pairs.
I've tried using categories to extend NSControl which is the common parent of all those widgets in the following way.
NSControl+formItemSupport.h
-------------------------------
#interface NSControl (formItemSupport)
#property NSString * formItemId;
#end
NSControl+formItemSupport.m
-------------------------------
#implementation NSControl (formItemSupport)
-(NSString *)formItemId {
return self.formItemId;
}
-(void)setFormItemId:(NSString *)formItemId {
self.formItemId = formItemId;
}
in the form.m file I import from NSControl+formItemSupport.m but when I try to set this field in NSButton : NSControl object. However, when I try to set the property formItemId, I get into infinite loop. Perhaps there's another way for extending objc class with variable based property without using inheritance ?
you can
#synthesize formItemId = _formItemId;
//synthesize needs local declaration of _formItemId;
#implementation ExtraWurst {
NSString *_formItemId;
}
but this is done behind the scene for you from Xcode without #synthesize.
Sometime it is still easier to define the use of an internal variable for a property in this way.
apart from that you can and have to change your setter and getter methods in the following way.
-(NSString *)formItemId {
return _formItemId;
}
-(void)setFormItemId:(NSString *)formItemId {
_formItemId = formItemId;
}
this will prevent you from ending up in a loop.
Why?
Because self.formItemId = refers to -(void)setFormItemId:
So you would call the setter inside the setter that will set with the same again and again aka an endless loop.
You can take care of the getter the same way as shown above.
Where to use self.yourProperty then?
You can use self.formItemId anywhere in the class but not inside getter and setter of formItemId.
Correctly mentioned, Instance variables may not be placed in categories.
Meaning if you need such you have to subclass UIControl but that breaks the inheritance of your used UIControls. You would have to subclass all your SpecialUIControls you are using later.
Another solution, you could define a constant in your implementation and go with objective-C runtime functions and associate this constant yourself. Beware because you transform the ObjectModel for all UIControl classes then..
#import "NSControl+formItemSupport.h"
#import <objc/runtime.h>
#implementation UIControl (formItemSupport)
NSString const *key = #"formItemSupport.forItemKey";
-(void)setFormItemId:(NSString *)formItemId {
objc_setAssociatedObject(self, &key, formItemId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)formItemId {
return objc_getAssociatedObject(self, &key);
}
#end
still, its much easier and safer and flexible to subclass your own UIControl instead to extent all subclasses inherited from UIControl.
Why is subclassing easier here?
As you mentioned you want to json later on with the given formItemId per Control you can make use of an archiver / unarchiver design pattern of your subclasses which are nice to jsonify later.
I'm trying to combine two plugins written in Objective-C. I have Plugin1 and Plugin2 which execute fine enough independently. I'm trying to add the Plugin2.m code to my Plugin1 Classes folder and execute both at the same time.
When I do this, Plugin1.m seems to execute first, I guess because it has IBAction calls and Plugin2.m doesn't? This is fine, but I'd like to run Plugin2.m within a function in Code1.m. So In the code below, when the IBAction call in Plugin1 is initiated I would like it to do what Plugin2 normally does and then continue with Plugin1 methods.
Plugin1.h:
#import Plugin2.h
#interface Plugin1: NSWindowController {
...
}
+(void) Plugin2;
#end
Plugin1.m:
#import "Plugin1.h"
#import "Plugin2.h"
#implementation Plugin1
-(id) loadPlugin1
{
...
}
-(IBAction) computeStuff:(id)sender
{
[self Plugin2];
//Plugin2* testRun = [Plugin2 alloc] init];
...do other stuff
}
#end
Plugin2.h
#interface Plugin2 : PluginFilter {
...
}
#end
Plugin2.m:
#import Plugin2.h
#implementation Plugin2
-(void) initPlugin
{
...
}
#end
Unfortunately I can't troubleshoot this from within Xcode, I have to install and test the plugin on my program to test. But when I keep an eye on Console and try the above I get "-[Plugin1 Plugin2]: unrecognized selector sent to instance 0x7....
in your plugin1 interface you defined
+(void)plugin2;
but implemented no method
+(void)plugin2 {
}
when you invoke + method you need to tell which class you call the method from because self refers to an object and not the class.
[self.class plugin2];
// OR
[Plugin1 plugin2];
Hints: try to follow naming conventions in objective-c.
This will help you distinguish if a definition is a -method: or Class
Consider reading about delegate design patterns and the use of <Protocols>
Also define special -initPlugin in your Plugin2 implementation and use a return type. Otherwise you initialise nothing.
-(instancetype)initPlugin;
I am pretty new to iOS and Swift and currently I'm facing a problem with writing a Unit Test. I have a class (let's suppose it is called A) which has (readonly property from Objective-C) and in my test I want to have object of this class to pass to the method which later does something with it. Oh, I don't have any initializers as well... My question is, how to test such think? Maybe I have to mock such object somehow?
------EDIT-----
Okay. My post wasn't quite precise. Okay, so I know only basics of Swift (unfortunately I don't have time now to learn Objective - C as I was asked to write sth in Swift). I have a class provided by some company in which I have a class (written in Objective-C) like:
#interface MainClassX : NSObject
#property (nonatomic, strong, readonly) NSString* code;
#property (nonatomic, strong, readonly) NSArray<XYZ*>* classification;
#end
And in my test want to create an object of this class and init at least 'code' property... but the setter is private so I can't do any 'inheritance trick'...? Is there any option to do it or should I do it another way? The problem is that I want to test a method which takes array of such objects and does sth with them.
It's pretty tricky because they wanted those properties to be readonly, why do you want to test them?
Regardless the purpose, you can do these steps:
1. Look into adding methods to that class using Category (in Objective C), or extension(in Swift).
2. Implement that new init method, set the code properpty using Key-Value Programming
I have managed to do it real quick in Objective C, it's pretty straight forward to convert to Swift.
#implementation MainClassX(Test)
-(instancetype)initWithCode:(NSString *)code {
self = [self init];
if (self) {
[self setValue:code forKey:#"code"];
}
return self;
}
#end
Test it:
MainClassX *test = [[MainClassX alloc] initWithCode:#"TEST"];
NSLog(#"code: %#", test.code); // Should print out "TEST" in the console
Swift:
extension MainClassX {
convenience init(_ code: String) {
self.init()
self.setValue(code, forKey: "code")
}
}
In the unit test:
import XCTest
#testable import YourAppModule
class YourAppModuleTests: 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.
let cls = MainClassX("TEST")
XCTAssert(cls.code == "TEST")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
You are likely looking for dependancy injection. This is a way that you can initialize a class with an optional value that can set values for testing, as you want to do.
The following is a simple example.
Create an optional initialize for your Objective-C class:
- (instancetype)initWithOption:(NSString *)option {
self = [super init];
if (self) {
self.option = option;
}
return self;
}
You can have it such that when you normally call the class, you call its default init. But for testing, initialize it with this function. Another thing to consider if you may want to have a protected header file (such as classname_protected.h) which you only use in your unit tests so that you do not expose this function to your application.
Without seeing more of your test, it is a bit difficult to add on to this, but DI is likely where you need to go for it.
I am having a little problem. My scenario is: I will build a project with a lot of target. I want have the "public code" (for all target) and "specific code". The problem is: In "public code", I NEED call function at "specific code".
My first try was using categories. I create "public.h" then "public+specific.h" codes using categories. The class that will use this class will need to:
#import "public+specific.h"
...
public *myClass = [[public alloc] init];
[myclass doSomething];
To use another specific class, i only need to change the #import and nothing more. The unique problem is that in "public class" i will need create a false function, like.
//public.h
#interface public : NSObject {}
...
- (void) doSomething {return };
//public+specific.h
#interface public (specific)
...
- (void) doSomething { //do what it really have to do };
The other problems is intrinsic to categories: I can't create local class variable, all will have to be declared in "public.h". I want have all specific things IN specific class...
Ok, so I try in another way: use Inheritance with delegates. In the classes "public.h" and "public+specific.h" it work very well, no need to use fake function, all was fine. BUT, (aways a but), I always will have to alloc the specific class, and if I don't want this, I can create a fake function only to call the delegate, so I have the same problem above. This is a sample:
//In public.h
#protocol publicDelegate
-(void)doSomething;
#end
#interface public : NSObject {
id <publicDelegate> myDelegate;
}
-(id)initWithDelegate (id <publicDelegate>)initDelegate{
myDelegate = initDelegate;
[myDelegate doSomehing];
}
//public+specific.h //The '+' isn't correct here :P
#include public.h
#interface public_specific : public <publicDelegate> {}
- (id)init{
return [super initWithDelegate:self];
}
- (void) doSomething { //do what it really have to do };
Like I say, the problem here is how I create this object
#import "public+specific.h"
...
public_specific *myClass = [[public_specific alloc] init];
[myClass doSomething];
With this, I will have to create a lot of #if defined , #elif defined... every time that I need to create a object call. With categories, I only need to do this with the "#include".
To solve this problem, I can have things like this:
//in "public.h"
- (void) doSomething {
return [myDelegate doSomehing]
};
Another time I will create fake function. And worst, for every new function in "public+specific.h" I will have to create another fake function.. zzz.. (in categories, i have to do this only with function with "public.h" call in "public+specific.h")
So, anyone have another idea to this problem?? It's a little problem, but I want to make my code good, easy to develop and clean...
in many cases, composition would serve you well.
I would like to be able to use bindings to keep my GUI synchronized to a dynamically loaded object, but as soon as I replace the object in question with another one of the same type the bindings break and the GUI stops updating. Here's some code to help you understand what I mean:
In my interface I have an instance variable to hold the object in question:
#interface AppDelegate : NSObject {
CustomObject *anObject; // This object has a "NSString *textValue" property
}
Then in my implementation I instantiate the object:
- (id) init {
self = [super init];
if (self != nil) {
anObject = [[CustomObject alloc] init];
}
return self;
}
In Interface Builder I have the "value" of a text field bound to "anObject.textValue".
When I call this method:
[anObject setValue:#"Changed anObject.textValue!" forKey:#"textValue"];
then the text field updates to reflect the new value.
But what I want to do is display the values from an object which is given after doing some work elsewhere in the application. So what I did was this:
- (void)setCustomObject:(CustomObject *)newObject {
anObject = newObject;
}
Now the result of this operation seems to break the bindings from the GUI to the CustomObject instance (anObject) which seems logical considering the bound object has been replaced by another instance.
What I want to know is if there is a way to keep the bindings functional with the dynamically created instance of CustomObject without having to re-bind every control programmatically through bind:toObject:forKeyPath:options: or similar which would require (to my knowledge) the use of IBOutlets to get a hold of the controls to then be able to bind them to the values in my new object (IMO this would make the bindings kind of useless in my situation). Is this the only solution or is there a better, cleaner way to deal with this?
I have read a good bunch of documents on developper.apple.com and elsewhere regarding bindings but I did not find anything which seems to talk about this particular case.
Thanks in advance for your time!
To be specific, I think the problem was that your setter method was called -setCustomObject: instead of -setAnObject:. If you made just that change I think that KVO would be invoked, and your bound textfields would be updated.
Abizern's note about it leaking (if you're not using GC) still applies though. Your setter should instead look something like:
- (void)setAnObject:(CustomObject *)newObject {
if (anObject != newObject) {
[anObject release];
anObject = [newObject retain];
}
}
Have a look at these docs on Key Value observing. This should show you how to change properties in a KVO compliant way.
Alternatively, set up anObject as a property:
#interface AppDelegate : NSObject {
CustomObject *anObject; // This object has a "NSString *textValue" property
}
#property (retain) CustomObject *anObject;
...
#end
#interface AppDelegate
#synthesize anObject;
...
#end
Then when changing the anObject instance, use property syntax.
self.anObject = newObject;
This will take care of the KVO stuff for you.
note:
Unless you have GC turned on your setCustomObject: method leaks.