square / retrofit

A type-safe HTTP client for Android and the JVM
https://square.github.io/retrofit/
Apache License 2.0
43.11k stars 7.3k forks source link

How run unit test with ActivityInstrumentationTestCase2 + Mockito ? #1220

Closed billyjoker closed 9 years ago

billyjoker commented 9 years ago

i am using Retrofit successfully and currently i'm facing with ActivityInstrumentationTestCase2 to test my app therefore Retrofit. With classical test cases i'm running ok, but i have no the right concepts to run a Retrofit test though i've researched through the web, but the cases i've figured out has no coincidence with my scenario. This is my method i'd like to test:

    Call<Geonames> callRetrofit = Retrofit_CiudadApi.getInstance().getMetaCiudad(editCiudad.getText().toString());
    callRetrofit.enqueue(new Callback<Geonames>() {
        @Override
        public void onResponse(Response<Geonames> response) {
            Geonames geoCiudades = response.body();
            Log.i(TAG, "url " + response.raw().request().httpUrl());
            if (geoCiudades == null) {
                Log.e(TAG, "error obteniendo datos ciudad");
                btn_mapa.setVisibility(View.INVISIBLE);
            } else {
                Log.i(TAG, "success obteniendo datos ciudad");
                if (geoCiudades.geonames != null && geoCiudades.geonames.size() > 0) {
                    btn_mapa.setVisibility(View.VISIBLE);
                    interfrag.setGeoname(geoCiudades.geonames.get(0));
                    Interface_APIGeoTemp apiCity = new API_GeoTemp();
                    apiCity.consultaTemperaturaCiudad(FrmTemperatura.this, geoCiudades.geonames.get(0));
                }
            }
        }
        @Override
        public void onFailure(Throwable t) {
            Log.e(TAG, "throbale: " + t.getMessage());
        }
    });

Now, in my MainActivityTest which extends ActivityInstrumentationTestCase2 i added some code as follows but i do not know exactly how should i proceed:

@Mock private Call mockApiReq;

public void dummyTest() { Mockito.when(mockApiReq.enqueue???).thenReturn(???); }

I do not know how pass the parameters and the response to Mockito or an alternative solution to this.

Thanks!!

tomkoptel commented 9 years ago

Hello @billyjoker !

So far you are trying to mock out server call. There is no simple solution for functional tests you are trying to implement. I can suggest you to use dependency injection framework Dagger 2 + MockWebServer. Basic idea involves this steps:

    @Before
    public void setup() throws Exception {
        server = new MockWebServer();
        server.start();

        String mockUrl = server.url("/").url().toExternalForm();
        YourApplication app = ((YourApplication) getInstrumentation().getContext().getApplicationContext());
        app.setComponent(MockComponent.with(mockUrl));
    }

    public void testDummy() {
        MockResponse response = new MockResponse().setBody("{\"foo\": \"bar\"}");
        server.enqueue(response);

        // invoke map activity. For example, do this with espresso
    }

    @After
    public void teardown() throws Exception{
        server.shutdown();
    }
JakeWharton commented 9 years ago

This is a high-level usage question which is better suited to StackOverflow. There's a wide variety of ways that one could alter Retrofit for use in an Android integration test, and the right way is certainly beyond the scope of this project.

At the very least, you're using the dependency locator pattern which is pretty detrimental for its ability to be tested, and something like leveraging inversion of control via dependency injection (either as a pattern or with a library) will allow for code that is more easily testable. With that, the interface could be swapped out instead of having to know how to go out and find the instance to use.