我的代码调用了一个C库函数:

@implementation Store
  ...
  -(void) doWork {
    // this is a C function from a library
    int data = getData();
    ...
  }
end

我正在对上面的函数进行单元测试,我想在测试中模拟C函数getData(),这是我的测试用例:
@interface StoreTests : XCTestCase {
    int mData;
    Store *store;
}
@end

@implementation StoreTests

-(void) setUp {
  [super setUp];
   mData = 0;
   store = [[Store alloc] init];
}

-(void) testDoWork {
  // this call will use the mocked getData(), no problem here.
  [store doWork];
}

// mocked getData()
int getData() {
   mData = 10; // Use of undeclared identifier 'mData', why?
   return mData;
}

...
@end

为什么会出现编译器错误:
Use of undeclared identifier 'mData'函数里面模拟了getData()

最佳答案

您误解了实例方法和变量的工作方式。

每个实例方法都有一个引用当前实例(或“当前对象”)的self变量,并且实例变量(例如mData)的使用是使用self(例如self->mData)访问该变量的简写,其中->是(Objective-现场访问的C操作员。因此,您写成“长手”的setup方法是:

-(void) setUp {
   [super setUp];
   self->mData = 0;
   self->store = [[Store alloc] init];
}

但是对实例的引用self本身从哪里来?好吧,它不是神奇的,只是隐藏的,它会作为隐藏的额外参数自动传递给实例方法。此时切换到伪代码以显示此内容。您的setup方法有效地编译为:
-(void) setUp withSelf:(StoreTest *)self {
   [super setUp];
   self->mData = 0;
   self->store = [[Store alloc] init];
}

和一个电话,例如:
StoreTests *myStoreTests = ...

[myStoreTests setup];

被有效地编译为:
[myStoreTests setup withSelf:myStoreTests];

自动添加额外的self参数。

现在,以上所有内容仅适用于方法,并使它们能够访问实例变量和方法,不适用于普通C函数-它们没有隐藏的self参数,并且无法访问实例变量。

您在添加的答案中提到的解决方案,该解决方案是在接口外部声明mData的:
int mData;
@interface StoreTests : XCTestCase {
   Store *store;
}
@end

mData更改为全局变量,而不是实例变量。 C函数可以访问全局变量。但这确实意味着该类的每个实例共享相同的mData,在这种情况下只有一个mData,而不是每个实例一个。

因此,将实例变量设置为全局变量并不是解决此类问题的通用解决方案,但是,由于您不太可能拥有多个StoreTests类实例,因此在这种情况下,这是一个合适的解决方案。

但是,您应该进行一项更改:程序中只能有一个具有给定名称的全局变量,因此mData必须是唯一的,并且可以被程序中的任何代码访问,而不仅仅是StoreTests的代码。您可以通过将变量声明为static来减轻这种情况:
static int mData;

这将变量保留为全局变量,但仅使其对声明中的同一文件中的代码可见,这可能只是StoreTests的代码。

高温超导

09-20 10:22