Search code examples
objective-cunit-testingassertxctest

Unit testing the value of a static constant using XCTest?


Simple question: I’m trying to use XCTest to assert the value of some static constants – my QA engineer has asked me to do this just in case another engineer goes in and manually changes them.

To my surprise, I’m writing a super simple assert to ensure the value is the hardcoded value I set it to be, in this case 768.0f, but the test’s failing, and seeing it as zero.

In the .h file:

static CGFloat const BLABannerViewExpectedWidthPad;

In the .m file:

static CGFloat const BLABannerViewExpectedWidthPad = 768.0f

These are outside the @interface and @implementation blocks, as normal for declaring them in Objective-C.

Test:

- (void)test_constants {
    XCTAssertEqual(BLABannerViewExpectedWidthPad, 768.0f);
}

This fails, 0 is not equal to 768.

Very strange, am I doing something stupid, or can we not unit test this?


Solution

  • Constants need to be defined as extern in the header, and not static in either file.

    *.h:

    extern CGFloat const BLABannerViewExpectedWidthPad;
    

    *.m:

    CGFloat const BLABannerViewExpectedWidthPad = 786.0;
    

    The way this works is that extern tells the current compilation unit (the file which imported the header) that there is a variable somewhere with the given definition. The compiler takes your word for it and leaves reconciliation to the linker.

    The linker finds an actual storage definition with the given symbol (in the *.m), and fixes up addresses in all the users to refer to that same storage.

    When you define a global variable as static, you are limiting its scope to the current compilation unit (*.m). This hides the symbol from the list of global symbols the linker sees. When this is done in a header file which is included into many compilation units (*.m), each unit gets its own private symbol with the same name. Since only one *.m actually initializes the variable, only that copy has a value.