Spurious Test Failures using Picasso and Robolectric

故事扮演 提交于 2019-12-06 01:39:32

Drew is correct. The NPE is occurring in a background thread.

Caused by: java.lang.NullPointerException: Bitmap config was null.

Fundamentally this seems to be an issue with ShadowBitmap. However the solution I opted for was to create a MockPicasso class.

The stub implementation prevents the NPE. It also has the added benefit of preventing the unit tests from requesting bitmaps over the network in the first place.

MockPicasso.java

package com.squareup.picasso;

import android.graphics.Bitmap;
import android.widget.ImageView;

public class MockPicasso extends Picasso {
    private static String lastImagePath = null;
    private static ImageView lastTargetImageView = null;

    MockPicasso() {
        super(null, null, null, Cache.NONE, null, new MockStats());
    }

    public static void init() {
        singleton = new MockPicasso();
    }

    public static String getLastImagePath() {
        return lastImagePath;
    }

    public static ImageView getLastTargetImageView() {
        return lastTargetImageView;
    }

    @Override
    public RequestBuilder load(String path) {
        lastImagePath = path;
        return new MockRequestBuilder();
    }

    class MockRequestBuilder extends RequestBuilder {
        @Override
        public void into(ImageView target) {
            lastTargetImageView = target;
        }
    }

    static class MockStats extends Stats {
        MockStats() {
            super(Cache.NONE);
        }

        @Override
        void bitmapDecoded(Bitmap bitmap) {
            // Do nothing.
        }
    }
}

Using getLastImagePath() and getLastTargetImageView() you can test your code is requesting the correct image and loading it in the correct view without actually hitting the network.

MyActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    ImageView avatar = (ImageView) findViewById(R.id.avatar);
    String path = getIntent().getStringExtra("avatar_url");
    Picasso.with(this).load(path).into(avatar);
}

MyActivityTest.java

@Test
public void shouldDisplayAvatar() throws Exception {
    MockPicasso.init();
    String imageUrl = "http://www.example.com/test.jpg";
    Intent intent = new Intent().putExtra("avatar_url", imageUrl);
    MyActivity myActivity = Robolectric.buildActivity(MyActivity.class)
            .create().get();

    myActivity.setIntent(intent);
    Robolectric.shadowOf(myActivity).callOnCreate(null);
    ImageView avatar = (ImageView) myActivity.findViewById(R.id.avatar);
    assertThat(MockPicasso.getLastImagePath()).isEqualTo(imageUrl);
    assertThat(MockPicasso.getLastTargetImageView()).isSameAs(avatar);
}

The warning isn't lying.

Intent intent = new Intent( new ActivityUnderTest(), Activity.class );

The "new ActivityUnderTest()" qualifies as "instantiating an activity directly."

In many cases where tests fail/pass seeming-randomly, there is a threading issue in the underlying code. The last time this happened to me the "sometimes this fails" test was actually exposing a race condition in my queue code.

I find that when I have the reaction "this test is stupid!" I almost discover that the test is actually smarter than me (and nicer than me, too, as it is trying to be helpful).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!