UnsatisfiedLinkError when unit testing WritableNativeMap
Asked Answered
S

2

10

I am currently creating an Android library for use in a react native project. I need to emit a map to javascript, so I'm using react native's WriteableMap class. Unfortunately, the class loads the reactnativejni SO in a static block, which leads to an UnsatisfiedLinkError during unit tests. I'm using JUnit and Mockito to test.

My code:

@Override
public void onSomething() {
    WritableMap params = Arguments.createMap();

    //fill map

    sendEvent("onChange", params);
}

The error:

java.lang.UnsatisfiedLinkError: no reactnativejni in java.library.path
  at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)
  at java.lang.Runtime.loadLibrary0(Runtime.java:870)
  at java.lang.System.loadLibrary(System.java:1119)
  at com.facebook.soloader.SoLoader.loadLibrary(SoLoader.java:172)
  at com.facebook.react.bridge.NativeMap.<clinit>(NativeMap.java:23)
  at com.facebook.react.bridge.Arguments.createMap(Arguments.java:29)
  at me.MyClass.onSomething(myClass.java:23)

I started using the Arguments.createMap() method after seeing a comment about stubbing WriteableMap for unit tests, but it's static and I would prefer to not have to stub a static method.

Is there some way to get rid of this error when running unit tests?

Satirize answered 8/2, 2016 at 17:33 Comment(0)
O
9

I don't think you can avoid mocking the Arguments methods in a unit test (though I believe you do not need to do so in an instrumentation test).

In Facebook's own tests, they use PowerMockito to mock them: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java#L67

Interesting bits:

PowerMockito.mockStatic(Arguments.class);
PowerMockito.when(Arguments.createArray()).thenAnswer(new Answer<Object>() {
  @Override
  public Object answer(InvocationOnMock invocation) throws Throwable {
    return new JavaOnlyArray();
  }
});
PowerMockito.when(Arguments.createMap()).thenAnswer(new Answer<Object>() {
  @Override
  public Object answer(InvocationOnMock invocation) throws Throwable {
    return new JavaOnlyMap();
  }
});

Also note that this requires you to modify your build.gradle to include these mocking tools: https://github.com/facebook/react-native/blob/master/ReactAndroid/build.gradle#L266

dependencies {
  ...
  testCompile "org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}"
  testCompile "org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}"
  testCompile "org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}"
  testCompile "org.mockito:mockito-core:${MOCKITO_CORE_VERSION}"
  testCompile "org.easytesting:fest-assert-core:${FEST_ASSERT_CORE_VERSION}"
  testCompile "org.robolectric:robolectric:${ROBOLECTRIC_VERSION}"
  ...
}

The versions they use can be found in their gradle.properties file.

I don't know how stable these test configurations will be over the long run, but this configuration has let me work with ReadableMap/Array and WritableMap/Array in unit tests.

Osteoclast answered 8/4, 2016 at 16:43 Comment(2)
Wondering what I'm doing wrong. I setup PowerMockito with all it's dependencies and also @Before setUp, but test still fails on no reactnativejni in java.library.path, but this time it's on line PowerMockito.when(Arguments.createArray())Rustproof
I just quickly scanned through the code and I think without reactnativejni it simply will not work. PowerMockito.when(Arguments.createMap()) will eventually call ReactBridge.staticInit which calls SoLoader.loadLibrary("reactnativejni"); So even if mocking is probably later possible, any it's useless as setup mock will fail on ReactBridge.staticInit.Rustproof
S
2

You can also use JavaOnlyMap, it implements both WritableMap and ReadableMap.

So wherever you need to use Arguments.createMap(), just replace with new JavaOnlyMap()

reference: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java

Septempartite answered 6/2, 2019 at 7:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.