Native unit testing in Xcode

Hi, I’m trying to add some native testing to my iOS app by using Xcode unit tests. But any time I make a reference to any of my classes, the compiler can’t find them. I’ve tried importing my main module (ios-moe) but that doesn’t work. So I looked up the actual module name of the ios-moe module (the name of my app) and used that in the import statement, but it still doesn’t recognize it.

I know the .h and .m versions of my Java classes have to exist at some point in order for the code to be bridged properly (at least I think). Besides my IOSLauncher (I’m using LibGDX) and some of my pure Java classes, I also have some Java classes that are used for Interface Builder, but I can’t find a way to reference them in my tests either. Seems like they are defined in “moe-main-interfaces.m” but I can’t figure out how to get native test files to acknowledge their existence.

My pseudo-native Java classes even have an ObjC mapping at the top of the class, e.g.

@Runtime(ObjCRuntime.class)
@ObjCClassName("SettingsController")
@RegisterOnStartup

But when I try to import or reference SettingsController, I just get an error that it can’t be found or it’s an undeclared identifier.

Has anyone tried native Xcode testing with an MOE project before? I really want to do it natively through Xcode instead of using something like JUnit through Android Studio if possible.

If there are any relevant MOE samples or solutions please point me in their direction. I’ve looked around for anything related to this question but documentation seems to be scarce.

Just to be clear, I haven’t created any .h or .m files for my app. All of my classes are pure Java, with the Interface Builder magic being handled by MOE.

Thanks!

Hi MrPat,

Before we go down this “rabbit hole” could you please elaborate why you don’t want to use JUnit?

TL;DR: What you are trying to do could work but it is largely untested, so you will probably run into some bugs lurking in the shadow.

The Java classes are only added to the Objective-C runtime at runtime: this means, that they don’t exist as .m or .h before that. The moe-main-interfaces.m file includes #ifdef guards, so this file is not included in the build -> so the compiler can’t pick its contents up either - but Interface Builder / Storyboards will work with it, so it serves its purpose.

Technically, if you remove these guards manually it should still work, so your compiler could find these classes and you could reference them from your tests, and when Nat/J initializes (see below for that issue), it will merge with the already existing ObjC stubs. That said, we usually support this method the other way around: you create your empty ViewControllers in ObjC, and then use Natjgen to create the bindings in Java, which you extend in Java (so they actually become hybrid classes and not real bindings). Also, our Java -> ObjC stub generator was not intended to create a complete representation of the Java class in ObjC, only the things required for Interface Builder are generated, so some things could be missing that you want to test.

Another issue: I assume that the Xcode tests use a different entry point and not main.m. This means that the moevm() call is not executed, so the Java runtime will not be initialized when the tests run. One would need to call moevm() exactly once before running the tests to make sure that the Java runtime is initialized. Also: it would need to be called in a way to make sure that it returns (i.e. the Java main method exits), but the runtime stays initialized, so your @RegisterOnStartup classes can be found by the Objective-C runtime.

I recall, that we did something like this earlier (it was like in early 2016), but this is not something we test regularly, so I am not sure if it is broken or not.

Best Regards,
Gergely

1 Like

Thanks for the detailed reply Gergely! I was integrating fastlane and saw that it had an option to run tests automatically, so that’s why I wanted to try the native testing. That and I figured it would work better for things like UI testing, but it sounds like a bad option now.

Would something like JUnit work properly with MOE or is there some configuration that needs to be done? I’m open to that if it’s easier than native Xcode testing. I could probably script in a gradle task to run the tests from fastlane.

Hi,

sure, JUnit should work out of the box, we have a special JUnit runner included in MOE to run the tests on the device or on the simulator.

You can either run it from your IDE, or using Gradle:

./gradlew moeTest <options> 

The options are the same as with the moeLaunch task, documented here:

On the native side, the <modulename>-Test target is used when JUnit is running.

Best Regards,
Gergely