I am looking to structure my unit tests in the correct way and would like to know how to approach testing a class method while passing an object into the method
So the method in question (basic example)
MyClient.m:
#import "MyClient.h"
#interface MyClient()
#property (nonatomic, strong) MyMedia *myMedia;
#end
- (void)setMedia:(MyMedia *)media {
self.MyMedia = media;
int var;
if (media && media.isLive) {
var = 1;
}
else {
var = 2;
}
}
}
So in this example i would create two tests, 1 that tests var is 2 if media.isLive and the other to check var is 2 if not.
Hopefully this makes sense
Thanks
Your code is not really testable because everything you would need to check is private. Whilst there are ways to 'hack' into a class using the runtime and stuff to test things, you really want to just utilise the publically access interface of your class. So I've re-written your code like this:
MyClient.h
#interface MyClient:NSObject
#property (nonatomic, strong) MyMedia *myMedia;
#property (nonatomic, assign, readonly) int var;
#end
MyClient.m
#import "MyClient.h"
#implementation MyClient
- (void)setMedia:(MyMedia *)media {
_MyMedia = media;
_var = media && media.isLive ? 1 : 2;
}
#end
Now lets do some tests using XCTest.
MyClientTests.m
#import XCTest;
#import "MyClient.h"
#interface MyClienTests:XCTestCase
#end
#implementation MyClientTests {
MyClient *_myCLient;
MyMedia *_media;
}
-(void) setUp {
_myClient = [[MyClient alloc] init];
_media = [[MyMedia alloc] init];
}
-(void) testMyClientMediaIsAlive {
_media.alive = YES;
_myClient.media = _media;
XCTAssertEqual(1, _myClient.var);
}
-(void) testMyClientMediaIsNotAlive {
_myClient.media = _media;
XCTAssertEqual(2, _myClient.var);
}
-(void) testMyClientNoMedia {
XCTAssertEqual(2, _myClient.var);
}
#end
Related
I am converting my Objective-C code into Swift.
I have done all required things like: In Bridging-header imported the “DCclass.h" etc. Things work fine but while setting tag I am facing an issue, searched for this on Google but did not get a solution. Thanks in Advance.
Current Objective-C code:
The .h file:
#import <UIKit/UIKit.h>
#import “DCclass.h”
#interface LPFPickerCell : UITableViewCell <UITableViewDelegate, UIPickerViewDelegate,UIPickerViewDataSource>
- (void)setDelegate:(id) delegate;
- (void)setMyMutableArray:(NSMutableArray *) mutArray;
- (void)setSelectedData:(NSString *) selectedString
#end
The .m file:
#import “DCclass.h”
#interface LPFPickerCell ()
#property (nonatomic) NSMutableArray *myMutableArray;
#end
#implementation LPFPickerCell
- (void)setTag:(NSInteger)tag
{
self.m_Tag = tag;
}
- (void)setSelectedData:(NSString *) selectedString
{
int i = 0;
for (DCclass *dc in self.myMutableArray)
{
NSLog(#"DCclass.empName = %#",dc.empName);
}
}
#end
Converted Swift Code:
class LPFPickerCell {
var myMutableArray = [AnyHashable]()
var m_Tag: Int = 0
func setTag(_ tag: Int) { /// This line is showing error same as Question post title described above.
m_Tag = tag
}
func setSelectedData(_ selectedString: String?) {
let i: Int = 0
for dc: DCclass in myMutableArray {
print("DCclass.empName = \(dc.empName)")
}
}
}
Change the name of your method, it says that an existing method that has the name setTag already exist for the UIView swift class.
Just change setTag to something like setCellId or what you want.
// Marketplace.h
#import <Foundation/Foundation.h>
#import "Item.h"
#interface Marketplace : NSObject
+ (void)addItemToMarketplace:(Item *)newItem; // METHOD IN QUESTION
#end
// Marketplace.m
#import "Marketplace.h"
#interface Marketplace()
#property (strong, nonatomic) NSMutableArray *listOfItems;
#end
#implementation Marketplace
+ (void)addItemToMarketplace:(Item *)newItem // METHOD IN QUESTION
{
[self.listOfItems addObject:newItem]; // Raises 3 errors
}
#end
I have declared a class method addItemToMarketplace that takes in an object of type Item and adds this Item to the listOfItems property that I have declared in the interface of the implementation file (I am not sure that I want other classes to fiddle with this property). I have used this method in another class as such [Marketplace addItemToMarketplace:newItem]. I am not sure how to handle the three errors that are raised when I write [self.listOfItems addObject:newItem].
The 3 errors are as follows:
1. Member reference type 'struct objc_class *' is a pointer; maybe you meant to use '->'?
2. Definition of 'struct objc_class' must be imported from module 'ObjectiveC.runtime' before it is required
3. No member named 'listOfItems' in 'struct objc_class'
Making the change proposed in #1, which changes self.listOfItems to self->listOfItems, raises the error "Member reference base type 'Class' is not a structure or union"
Any help would be appreciated.
// NEW CHANGES!
After making some changes and following some suggestions I found on other sites, here's what I have so far:
// Marketplace.h
#import <Foundation/Foundation.h>
#import "Item.h"
#interface Marketplace : NSObject {}
+ (Marketplace *)sharedMarket;
- (void)addItemToMarketplace:(Item *)newItem;
#end
// Marketplace.m
#import "Marketplace.h"
#interface Marketplace()
#property (strong, nonatomic)NSMutableArray *listOfItems;
#end
static Marketplace *sharedMarketplace = nil;
#implementation Marketplace
+ (Marketplace *)sharedMarket
{
if (sharedMarketplace == nil) {
sharedMarketplace = [[super allocWithZone:NULL] init];
}
return sharedMarketplace;
}
- (void)addItemToMarketplace:(Item *)newItem
{
[self.listOfItems addObject:newItem];
}
- (id)init
{
if ( (self = [super init]) ) {
}
return self;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedMarket];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#end
Questions, comments, concerns?
Only the instance methods can operate on instance data. So you need either an instance or change the data to static too:
#interface Marketplace()
#end
static NSMutableArray *listOfItems; // static is implied, so not really necessary
#implementation Marketplace
+ (void)addItemToMarketplace:(Item *)newItem {
if (!listOfItems) listOfItems = [NSMutableArray array];
[listOfItems addObject:newItem];
}
#end
Note that there's no thread safety here.
The + prefix defines a class method. Within that method self refers to the class itself which is almost certainly not what you intend. The class does not have a listOfItems as that is a property available on each instance of the class.
It is not clear what you are attempting to do here. If you are coming from a C/C++ background perhaps you have assumed a different behavior for this method. Are you trying to add an item to a list managed by a particular instance of this class or to a list shared across all instances?
If you just have one instance of marketplace in your app consider using a singleton..
You then might add items to the single marketplace like this:
[[Marketplace theMarketplace] addItem:<*item*>]
Here's one way to set this up:
#interface Marketplace : NSObject
#property ( nonatomic ) NSArray * items ;
#end
#implementation Marketplace
static Marketplace * __marketplace ;
+(void)load
{
__marketplace = [ Marketplace new ] ;
}
+(instancetype)theMarketplace
{
return __marketplace ;
}
-(void)addItem:(Item*)item
{
self.items = [ ( self.items ?: #[] ) arrayByAddingObject:item ] ;
}
#end
(or in Swift)
class Marketplace
{
struct Static
{
static let marketplace = Marketplace()
}
var items:Array<Item> = [] ;
class func get() -> Marketplace { return Static.marketplace }
func addItem( item: Item ) { self.items += item }
}
Is it possible to extend an derived class from NSManagedObject? I'm asking this because I tried to do it. My entity looks like this:
So this means a class similar to the following code should be generated:
#import <Foundation/Foundation.h>
#interface Player : NSManagedObject
#property (nonatomic, copy) NSNumber* orderNumber;
#property (nonatomic, copy) NSString *name;
#end
.m file
#import "Player.h"
#implementation Player
#dynamic name, orderNumber;
#end
This two variables are saved to the SQLite database.
Now since I need some additional variables for the player I just added them to the class. It still worked.
#import "Player.h"
#implementation Player
#dynamic name, orderNumber;
- (id) init
{
self = [super init];
if (self != nil)
{
[self reset];
}
return self;
}
#synthesize isStillInGame = _isStillInGame;
- (void) reset
{
_isStillInGame = TRUE;
}
- (void) setOutOfGame
{
_isStillInGame = FALSE;
}
#end
But now when I change the isStillInGame bool, all instances of the Player Class are changed. Is this normal or do I have an error in my code?
A second question I can't answer is, why I can't access the object variables while debugging. When I try to watch an Player instance variable I just see this:
Is it possible to see more?
I'm getting the error incompatible pointer types assigning to Deck *__strong from PlayCards *
And i'm not sure why is that. Its in the first method implemented (deck):
#import "CardGameViewController.h"
#import "PlayingCards.h"
#interface CardGameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *cardLabel;
#property (nonatomic) NSUInteger flipsCount;
#property (strong, nonatomic) Deck *deck;
#end
#implementation CardGameViewController
-(Deck *) deck {
if (!_deck) _deck = [[PlayingCards alloc] init];
return _deck;
}
-(void) setFlipsCount:(NSUInteger)flipsCount {
_flipsCount = flipsCount;
self.cardLabel.text = [NSString stringWithFormat:#"Flips:%d", self.flipsCount];
}
- (IBAction)flipCard:(UIButton *)sender {
sender.selected = !sender.isSelected;
self.flipsCount++;
}
#end
This is the header file(nothing going on here):
#import <UIKit/UIKit.h>
//#import "Card.h"
//#import "Deck.h"
//#import "PlayingCards.h"
#interface CardGameViewController : UIViewController
#end
And the PlayingCard class inheriting from Deck class..
this is the PlayingCards.m
#import "PlayingCards.h"
#implementation PlayingCards
#synthesize suit = _suit;
//modifying the contents getter so it will return array with the ranks and rank+suit
-(NSString *) contents {
NSArray *cardsRank = [PlayingCards rankStrings];
return [cardsRank[self.rank] stringByAppendingString:self.suit];
}
//creating a method to make sure we get validated suits
+(NSArray *) validSuit {
return #[#"♠",#"♣",#"♥",#"♦"];
}
//creating calss method to validate the rank
+(NSArray *) rankStrings {
return #[#"?",#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
//creating a new setter for suit to make sure we get the valitated suits, uding the validateSuit method
-(void) setSuit:(NSString *)suit {
if ([[PlayingCards validSuit] containsObject:suit]) {
_suit = suit;
}
}
//creating new getter for suit to make sure its not empty
-(NSString *) suit {
return _suit? _suit: #"?";
}
//creating a class method to make sure when user set the rank he will will
+(NSUInteger) maxRank {
return [self rankStrings].count - 1;
}
//creating a new setter to the renk to make sure the rank is validates
-(void) setRank:(NSUInteger)rank {
if (rank <= [PlayingCards maxRank]) {
_rank = rank;
}
}
#end
PlayingCards.h
#import "Card.h"
#import "Deck.h"
#interface PlayingCards : Card
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+(NSArray *) validSuit;
+(NSUInteger) maxRank;
#end
This line:
if (!_deck) _deck = [[PlayingCards alloc] init];
Should be:
if (!_deck) _deck = [[PlayingCardDeck alloc] init];
If the parent for Card is of class NSObject as you say, and given that PlayingCards inherits from Card, then you can't assign an instance of PlayingCards to a variable of type Deck*. That's what the compiler is telling you.
If you really need to do it, you have to write:
if (!_deck) _deck = (Deck*)[[PlayingCards alloc] init];
It would only be valid because in Objective-C the implementation is given at runtime and which method of which class is called is only decided at runtime when the message is dispatched. However, this pattern is very unusual and you better be certain that PlayingCards is implementing all the selectors that might be called on a Deck instance. A better way would be to use protocols.
You can define a protocol and then use:
id <myProtocol> deck = [[PlayingCards alloc] init];
Put in the protocol all the selectors you need.
Why can't you use this ?
PlayingCards* deck = [[PlayingCards alloc] init];
Is there a way to use something like
if (carRecord.status == CarRecord.statusRepaired) { // using a class constant
// ...
}
such as in a car repair shop, the carRecord object's state status is checked against the CarRecord class's constant. In Objective-C, is there such a way?
You would typically do this with an enum. For example:
//=== CarRecord.h:
typedef enum CarRecordStatus {
CarRecordStatusBroken = 0,
CarRecordStatusRepaired
} CarRecordStatus;
#interface CarRecord (NSObject) {
CarRecordStatus _status;
}
#property (nonatomic, assign) CarRecordStatus status;
#end
//=== CarRecord.m:
#implementation CarRecord
#synthesize status=_status;
- (void)someMethod {
if (self.status == CarRecordStatusRepaired) {
//...
}
}
#end
Here is how would you define it in .h file :
typedef enum CarRecordStatus {
CarRecordStatusBroken = 0,
CarRecordStatusRepaired,
} CarRecordStatus;
#interface MyClassName : NSObject
..interfacebody..
#end
Use it inside MyClassName or any other just import it that's it.