Search code examples
iosobjective-cxcodeunit-testingskpaymenttransaction

Modifying internal properties of NSObjects (specifically SKPaymentTransaction)


I'm trying to unit test our receipt verification server and while I can alter the internal API to avoid this problem, it means we're not fully testing the client API so I'd like to avoid that.

As part of our API, we pass through a SKPaymentTransaction and then pass the Transaction.transactionReceipt through to our server.

To test this correctly, I'd like to create an instance of SKPaymentTransaction with a transactionReceipt of my choosing (valid and invalid values).

Unfortunately, SKPaymentTransaction defines the transactionReceipt property as read only, and I cannot declare an extension/sub-class defining it as readwrite due to this.

I also don't seem to be able to cast the SKPaymentTransaction pointer to a char* to manually inject the values in memory as Xcode won't allow this under ARC.

Does anyone have an idea of how I can achieve what I'm looking for?

Thanks Lee


Solution

  • Turns out I can swizzle the transactionReceipt getter to inject my own data into the call.

    So I end up with something like

    -(void)test_function
    {
        SKPaymentTransaction* invalidTransaction = [[SKPaymentTransaction alloc] init];
    
        Method swizzledMethod = class_getInstanceMethod([self class], @selector(replaced_getTransactionReceipt));
        Method originalMethod = class_getInstanceMethod([invalidTransaction class], @selector(transactionReceipt));
    
        method_exchangeImplementations(originalMethod, swizzledMethod);
    
        // Call to receipt verification server
    }
    
    - (NSData*)replaced_getTransactionReceipt
    {
        return [@"blah" dataUsingEncoding:NSUTF8StringEncoding];
    }
    

    I wrote a blog post showing my process, and giving a bit more detail here.
    http://engineering-game-dev.com/2014/07/23/injecting-data-into-obj-c-readonly-properties/