Leem.fin Leem.fin - 5 months ago 13
iOS Question

How to access a static variable from a unit test class?

I have a static variable

COURSE_NAME
in my
School
class implementation file:

The header file:

@interface School {
...
}
@end


In implementation file:

static NSString *COURSE_NAME = @"secret-course";
@implementation School
...
@end


I created a unit test class for
School
.

@interface SchoolTest : XCTestCase
@end

@implementation SchoolTest

- (void)setUp {
[super setUp];
...
}

- (void)tearDown {
...
[super tearDown];
}

- (void)testSomeFunc {
// how can I access the static variable 'COURSE_NAME'
// in School implementation file?
}


Is there a way to access the static variable
COURSE_NAME
defined in
School
implementation file from unit test class without exposing this static variable to
School
's public interface
?

(if the answer is no, then, what is the best practice to access a class' static variable from its unit test class? What kind of refactor to do? if the only solution is to declare
COURSE_NAME
to be non-static, then what is the best practice to refactor the code for my test case? Generally
COURSE_NAME
is a constant string, won't be changed.)

Answer

There is no way to access C-style static variables in Objective-C, because their names are local to translation unit - a fancy way of saying a .m file. The name of COURSE_NAME is not made available to the linker, which means that you cannot access them outside the .m file where they are declared.

There are three approaches that you could take to unit-testing the code relying on a static variable:

  1. Test public methods that rely on the COURSE_NAME variable, and check that the variable has a proper value in an indirect way.
  2. Make a non-static global test function that returns the value of the variable, and use that test function in your unit tests
  3. Add a class method (with + instead of - in front of its name) to your class, and use it to access the value of your static variable.

Since translation unit static variables are private to your implementation, #1 provides the best approach, so your unit tests do not rely on implementation details of your class.

The actual way you implement approach #1 depends on the way your code uses the value of COURSE_NAME.

For example, let's say that you use COURSE_NAME as a name of a required course that is always added to the list of courses that a student wants to take, unless it's already on the list. Then you should write a unit test that supplies a list of courses without a secret course on it, get the updated course list after the method runs, and add a check that "secret-course" is among the results. You should also add another test that includes "secret-course", and verifies that no new courses are added in this case.