Skip to main content

Unit Testing Capacitor Plugins

Unit testing a Capacitor plugin is similar to unit testing other APIs in an Android project. Just as you would mock network calls and validate inputs and outputs, unit testing for a Capacitor plugin involves mocking the Capacitor Plugin API and verifying the expected behaviors of plugin calls. This process helps ensure that the plugin functions as intended and maintains the desired behavior over time.

In this tutorial, you will see the types of unit tests commonly written for Capacitor plugins and examples of how to create effective unit tests for your own plugins.

Mocking Libraries

When it comes to unit testing the Capacitor Plugin API, effective mocking is essential. In this tutorial, we'll work with Mockito, a widely used mocking framework. However, you are free to choose a mocking library that aligns with your familiarity and preferences.

If you're keen on using Mockito for your tests, you can integrate it into your project by adding the following dependencies to your build configuration:

build.gradle

_10
testImplementation 'org.mockito:mockito-core:5.3.+'
_10
testImplementation 'org.json:json:20200518'

Unit Testing

CalculatorPlugin.java

_27
import com.getcapacitor.JSObject;
_27
import com.getcapacitor.Plugin;
_27
import com.getcapacitor.PluginCall;
_27
import com.getcapacitor.PluginMethod;
_27
import com.getcapacitor.annotation.CapacitorPlugin;
_27
_27
@CapacitorPlugin(name = "Calculator")
_27
public class CalculatorPlugin extends Plugin {
_27
_27
@PluginMethod
_27
public void add(PluginCall call) {
_27
Integer operandOne = call.getInt("operandOne");
_27
Integer operandTwo = call.getInt("operandTwo");
_27
_27
if(operandOne == null || operandTwo == null) {
_27
call.reject("Please provide two numbers.");
_27
return;
_27
}
_27
_27
Integer sum = operandOne + operandTwo;
_27
_27
JSObject ret = new JSObject();
_27
ret.put("sum", sum);
_27
_27
call.resolve(ret);
_27
}
_27
}

Plugin Implementation

We will write unit tests based on the implementation of CalculatorPlugin, a simple Capacitor plugin that contains a method that adds two numbers together.

CalculatorPlugin.java
Calculator.java

_29
import com.getcapacitor.JSObject;
_29
import com.getcapacitor.Plugin;
_29
import com.getcapacitor.PluginCall;
_29
import com.getcapacitor.PluginMethod;
_29
import com.getcapacitor.annotation.CapacitorPlugin;
_29
_29
@CapacitorPlugin(name = "Calculator")
_29
public class CalculatorPlugin extends Plugin {
_29
_29
private Calculator implementation = new Calculator();
_29
_29
@PluginMethod
_29
public void add(PluginCall call) {
_29
Integer operandOne = call.getInt("operandOne");
_29
Integer operandTwo = call.getInt("operandTwo");
_29
_29
if(operandOne == null || operandTwo == null) {
_29
call.reject("Please provide two numbers.");
_29
return;
_29
}
_29
_29
Integer sum = implementation.add(operandOne, operandTwo);
_29
_29
JSObject ret = new JSObject();
_29
ret.put("sum", sum);
_29
_29
call.resolve(ret);
_29
}
_29
}

Separation of Concerns

Capacitor Plugin calls return void, which makes it difficult to ensure the add() call is mathematically sound.

To make this plugin method more testable, the code should be refactored to separate concerns.

The Bridge design pattern is a great pattern to use when authoring Capacitor plugins for this very reason.

CalculatorPlugin.java
Calculator.java
CalculatorPluginTest.java

_14
import static org.junit.Assert.*;
_14
import static org.mockito.Mockito.*;
_14
import org.junit.Test;
_14
_14
public class CalculatorPluginTest {
_14
_14
private Calculator implementation = new Calculator();
_14
_14
@Test
_14
public void implementation_add_AdditionIsCorrect() {
_14
Integer result = implementation.add(5, 3);
_14
assertEquals(Integer.valueOf(8), result);
_14
}
_14
}

Testing Implementation

When the plugin code has its concerns separated, it becomes clearer to see what types of tests should be written for each distinct aspect of the functionality.

This modular approach not only enhances the clarity of the codebase but also guides developers in devising targeted and focused tests that validate the correctness of individual components.

As a result, the testing process becomes more efficient, manageable, and aligned with the underlying architecture, ultimately leading to more robust and reliable unit tests for your Capacitor plugin.

CalculatorPlugin.java
Calculator.java
CalculatorPluginTest.java

_41
import static org.junit.Assert.*;
_41
import static org.mockito.Mockito.*;
_41
import org.junit.Test;
_41
_41
public class CalculatorPluginTest {
_41
_41
private Calculator implementation = new Calculator();
_41
private CalculatorPlugin plugin = new CalculatorPlugin();
_41
_41
@Test
_41
public void implementation_add_AdditionIsCorrect() {
_41
Integer result = implementation.add(5, 3);
_41
assertEquals(Integer.valueOf(8), result);
_41
}
_41
_41
@Test
_41
public void plugin_add_ResolveWithGoodInput() {
_41
PluginCall mockCall = mock(PluginCall.class);
_41
when(mockCall.getInt("operandOne")).thenReturn(3);
_41
when(mockCall.getInt("operandTwo")).thenReturn(4);
_41
_41
plugin.add(mockCall);
_41
_41
verify(mockCall, times(1)).resolve(argThat(argument -> {
_41
JSObject expected = new JSObject();
_41
expected.put("sum", 7);
_41
return expected.toString().equals(argument.toString());
_41
}));
_41
}
_41
_41
@Test
_41
public void plugin_add_RejectWithBadInput() {
_41
PluginCall mockCall = mock(PluginCall.class);
_41
when(mockCall.getInt("operandOne")).thenReturn(null);
_41
when(mockCall.getInt("operandTwo")).thenReturn(4);
_41
_41
plugin.add(mockCall);
_41
_41
verify(mockCall, times(1)).reject("Please provide two numbers.");
_41
}
_41
}

Testing the Plugin Call

Since Capacitor plugins bridge functionality between web code and native mobile code, it's essential that plugin methods undergo rigorous unit testing to ensure that the input and output adhere faithfully to the plugin's API contract.

Consequently, the unit tests crafted to exercise the Capacitor Plugin API should be designed to verify that inputs and outputs adhere to the expected data types and structures.

By enacting these thorough tests, developers can affirm that the plugin's behavior aligns seamlessly with its intended usage, promoting stability, compatibility, and adherence to the established API conventions.

Plugin Implementation

We will write unit tests based on the implementation of CalculatorPlugin, a simple Capacitor plugin that contains a method that adds two numbers together.

Separation of Concerns

Capacitor Plugin calls return void, which makes it difficult to ensure the add() call is mathematically sound.

To make this plugin method more testable, the code should be refactored to separate concerns.

The Bridge design pattern is a great pattern to use when authoring Capacitor plugins for this very reason.

Testing Implementation

When the plugin code has its concerns separated, it becomes clearer to see what types of tests should be written for each distinct aspect of the functionality.

This modular approach not only enhances the clarity of the codebase but also guides developers in devising targeted and focused tests that validate the correctness of individual components.

As a result, the testing process becomes more efficient, manageable, and aligned with the underlying architecture, ultimately leading to more robust and reliable unit tests for your Capacitor plugin.

Testing the Plugin Call

Since Capacitor plugins bridge functionality between web code and native mobile code, it's essential that plugin methods undergo rigorous unit testing to ensure that the input and output adhere faithfully to the plugin's API contract.

Consequently, the unit tests crafted to exercise the Capacitor Plugin API should be designed to verify that inputs and outputs adhere to the expected data types and structures.

By enacting these thorough tests, developers can affirm that the plugin's behavior aligns seamlessly with its intended usage, promoting stability, compatibility, and adherence to the established API conventions.

CalculatorPlugin.java

_27
import com.getcapacitor.JSObject;
_27
import com.getcapacitor.Plugin;
_27
import com.getcapacitor.PluginCall;
_27
import com.getcapacitor.PluginMethod;
_27
import com.getcapacitor.annotation.CapacitorPlugin;
_27
_27
@CapacitorPlugin(name = "Calculator")
_27
public class CalculatorPlugin extends Plugin {
_27
_27
@PluginMethod
_27
public void add(PluginCall call) {
_27
Integer operandOne = call.getInt("operandOne");
_27
Integer operandTwo = call.getInt("operandTwo");
_27
_27
if(operandOne == null || operandTwo == null) {
_27
call.reject("Please provide two numbers.");
_27
return;
_27
}
_27
_27
Integer sum = operandOne + operandTwo;
_27
_27
JSObject ret = new JSObject();
_27
ret.put("sum", sum);
_27
_27
call.resolve(ret);
_27
}
_27
}