Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

call to Android API always returns 0 in a unit test

I am trying to calculate distance between two locations, I tried both location.distanceTo and Location.distanceBetween method but in both cases 0 is returned, I am not sure what am I doing wrong. Here is my method:

public static double distance(double lat1, double lon1, double lat2, double lon2, DistanceUnit unit) {
    Location location1 = new Location("");
    location1.setLatitude(lat1);
    location1.setLongitude(lon1);
    Location location2 = new Location("");
    location2.setLatitude(lat2);
    location2.setLongitude(lon2);
    // Distance in km
    double dist = location1.distanceTo(location2) / 1000;

    if (unit == DistanceUnit.Miles) {
        // Distance in miles
        dist = dist / 1.6;
    }
    return dist;
}

The unit test I am running to ensure that the test run is:

@Test
public void testDistance() {
    double lat1 = 40.2582279;
    double lon1 = -75.2654062;

    double lat2 = 40.476492;
    double lon2 = -75.355655;

    double distance = LocationUtils.distance(lat1, lon1, lat2, lon2, DistanceUnit.Kilometer);

    Assert.assertEquals(20, distance, 2);

}

And result I am getting is:

java.lang.AssertionError: 
Expected :20.0
Actual   :0.0

Thanks in advance

like image 581
Vibhu Avatar asked Aug 13 '17 12:08

Vibhu


1 Answers

Have you tried using the LocationUtils.distance() method in your app running on a phone/emulator? I would guess that it works as you'd expect in that situation.

When you run a unit test, it's being run on your computer, not on an Android device. You can't use the built-in Android classes (like Location). From the docs:

By default, the Android Plug-in for Gradle executes your local unit tests against a modified version of the android.jar library, which does not contain any actual code. Instead, method calls to Android classes from your unit test throw an exception. This is to make sure you test only your code and do not depend on any particular behavior of the Android platform (that you have not explicitly mocked).

It looks like you encountered this exception, which would have looked like this

java.lang.RuntimeException: Method setLatitude in android.location.Location not mocked. See http://g.co/androidstudio/not-mocked for details.
    at android.location.Location.setLatitude(Location.java)
    at example.com.stackoverflow.LocationUtils.distance(LocationUtils.java:13) 

Did you then read this which told you that you could add

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }
}

to your build.gradle file? This does prevent the android classes from throwing exceptions when you try to use them, but note that what this does is to

change the behavior so that methods instead return either null or zero

This is the exact behavior that you're seeing: location1.distanceTo(location2) returns 0.


More generally, this particular unit test seems unnecessary; you're mostly testing the Android API instead of testing your own logic. Unless there's part of your code do you want to make sure is tested? That you keep the correct lat/longs together? That the unit conversion works as expected? Perhaps you could extract the logic for whatever piece you're worried about and unit test that.

like image 50
Michiyo Avatar answered Sep 22 '22 14:09

Michiyo