Robolectric says mocks are not needed, but without mocks, Robolectric does nothing. I don't need it but mocks are painful.
Trying to test my Bluetooth Scanner which makes a call to BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner()
No matter what I do I always get this NullPointerException
java.lang.NullPointerException
at android.bluetooth.BluetoothAdapter.getLeState(BluetoothAdapter.java:950)
at android.bluetooth.BluetoothAdapter.getLeAccess(BluetoothAdapter.java:960)
at android.bluetooth.BluetoothAdapter.getBluetoothLeScanner(BluetoothAdapter.java:721)
How can one write units tests that involve Android Bluetooth APIs using Robolectric ... without mocking very single method in the Bluetooth stack? Isn't that the idea of Robolectric?
Here is my test which is an utter failure.
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.lni.codephg.inter.MdsIntermediary;
import com.lni.codephg.inter.MetricIntermediary;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.List;
import static org.robolectric.Shadows.shadowOf;
@RunWith(AndroidJUnit4.class)
//@RunWith(RobolectricTestRunner.class) // makes no difference which runner I use
@Config(sdk = Build.VERSION_CODES.P)
public class BtScannerTests
{
private BluetoothAdapter bluetoothAdapter;
//private final Context context = RuntimeEnvironment.systemContext;
@Before
public void setUp() throws Exception
{
// This does not change anything
PackageManager pm = context.getPackageManager();
shadowOf(pm).setSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE, true);
bluetoothAdapter = Shadow.newInstanceOf(BluetoothAdapter.class);
// Neither does this
shadowOf(bluetoothAdapter).setState(BluetoothAdapter.STATE_ON);
shadowOf(bluetoothAdapter).setEnabled(true);
}
private static boolean done = false;
@Test
public void testBtScannerCycle() throws InterruptedException
{
IntermediaryCallback intermediaryCallback = new IntermediaryCallback()
{
@Override
public void onReceiveMdsIntermediary(MdsIntermediary mds, int connectionHandle)
{
}
@Override
public void onReceiveMetricIntermediaries(List<MetricIntermediary> metricList, MdsIntermediary mds, int connectionHandle)
{
}
};
StatusEventCallback statusEventCallback = new StatusEventCallback()
{
@Override
public void onStatusEvent(StatusEvent statusEvent, int connectionHandle, String btAddress)
{
System.out.println("Status event " + statusEvent.name());
if(statusEvent == StatusEvent.SCANNING_PAUSED);
{
done = true;
}
}
};
Activity activity = Robolectric.buildActivity(Activity.class).get();
Context context = ApplicationProvider.getApplicationContext();
AndroidBtManager.setStatusEventCallback(statusEventCallback);
AndroidBtManager androidBtManager =
new AndroidBtManager(context, intermediaryCallback, false, false, true);
BtScanner btScanner = androidBtManager.getBtScanner();
while(!done)
{
Thread.sleep(1000);
}
}
}