Debugging and Testing

Debugging

J2ojbc generates Objective-C code, which can be run and debugged in Xcode like any other Objective-C code. However, as it is autogenerated, and certain features of Java are supported with difficult syntax, it can appear somewhat verbose and cryptic.

“Standard” methods are generally pretty straightforward. You’ll find your method names mapped to similar method names in the corresponding Objective-C file. You can set breakpoints in these directly. Alternatively, it is sometimes easier to set a breakpoint in your calling code and step in to the generated code in question.

Anonymous methods and lambdas, however, can be pretty tricky to find. Rather than appearing inline with the equivalent calling code, numbered classes are created and called. You can step into these from named methods, or find them manually with some hunting. However, complex call stacks can be pretty difficult to analyze. An RxJava call stack with multiple operators and lambdas, for example, can be pretty difficult to follow.

Logging

The complex call stacks can be difficult to follow visually, but they correspond to the source Java code, so adding logging statements can simplify your process.

Debugging Java Source

You can have J2ojbc output directives in the Objective-C code that will actually let you debug your running Objective-C code while looking at your Java code. It is Objective-C under the hood, but you’ll be looking at Java code, and be able to see call stacks and runtime values. It’s a little complex, but we’ll post a video soon walking you through it.

To turn that on, set the following inside the dopplConfig block.

emitLineDirectives = true

You’ll have to rebuild your code. The Objective-C source code actually has directives telling Xcode what Java file to look at and what lines.

Very Important The Java file references are absolute paths. You don’t want to check this into version control, and as of this writing, I don’t know if they have any impact on your runtime code. Summary, turn this off before you’re done.

Force Breakpoint

If you want to break at a certain point in your Java code but don’t want to dig for it, there’s a helper method for that. In the androidbasetest package, there’s a method on co.touchlab.doppl.utils.PlatformUtils called triggerXcodeBreakpoint. If this is called while running in an Xcode debugger session, your code will stop here. You’ll need to step up a couple steps, but you can then continute on. This is new! It currently doesn’t check that you’re in a debugger or even in iOS, so use caution.

Testing

J2ojbc comes with Junit. Creating basic Junit tests is simple. You should be able to call these without issue.

In the dopplConfig section of your gradle build file, add a value for copyTestOutput. See the sample app for an example.

dopplConfig {

    copyMainOutput "../ios/partyclickerframework/main", "../iostest/partyclickerframework/main"
    copyTestOutput "../iostest/iostestTests/test"

    copyDependencies true

    //Other stuff
}

When you run ./gradlew dopplDeploy, this should output test code which can be built and run in Xcode.

Running Tests

Test code will be translated by the Gradle plugin just like the main code. You compile the code in Xcode and run it in a simulator. However, you have to tell Junit what to run. There is a helper class called co.touchlab.doppl.testing.DopplJunitTestHelper. You can provide it a list of class objects, strings, or a text file resource with class names in it. You can also provide a class name and method name to run a specific method (format [classname]#[methodname], com.example.tests.FooTest#barMethod).

Test List Generator

The Doppl plugin will generate dopplTests.txt and copy that to your output directory. This file is generated when you run when you run dopplDeploy and have testing configured.

By default, the plugin assumes that you’ll have your test inside the src/test/java directory, and that your test files will be named something matching “**/*Test.java”. The files selected also respect the translatePattern setting, so organize your files accordingly.

You can customize the name by specifying testIdentifier in the dopplConfig block.

From Xcode

Add the dopplTests.txt file to the main application target’s Copy Bundle Resources Build Phase.

In your AppDelegate.m add:

[DopplRuntime start];
[CoTouchlabDopplTestingDopplJunitTestHelper runResourceWithNSString:@"dopplTests.txt"];

This will run all of tests in the classes listed in dopplTests.txt on app startup.

Command Line

You can run tests in Xcode from the command line. Although a little beyond the scope of this doc, here is an example.

Android Context

For some tests you’ll need the Android context available. In standard Android dev environments you’d probably either use the androidTest, or Robolectric. Our current solution is a bit of a hybrid. When running in iOS, it uses a Context created for testing (co.touchlab.doppl.testing.TestingContext), and when running in Java, it will delegate to Robolectric. You’ll need Robolectric in your dependencies or it will fail in Java.

Use DopplContextDelegateTestRunner.

@RunWith(DopplContextDelegateTestRunner.class)
public class DatabaseHelperTest
{

To get a referene to the context, call DopplRuntimeEnvironment.getApplication(). This will find the right instance for your environment.

For an example see:

DatabaseHelperTest.java and TestAppModule.java

Mockito

J2ojbc ships with a version of Mockito. However, it does not support ‘spy’. Just ‘mock’, and ‘spy’ will actually crash.

If you need spy in the short term, we’ve coded a very basic annotation processor that create mock objects. We’re hoping to have a functional Mockito implementation soon, but if you need it now see here.

Full Mockito support is high on the wish list, in case anybody wants a challenge.