Search code examples
javaandroidunit-testingrobolectricandroid-toast

Unit test for Toast message content using Robolectric


I have an Activity that does nothing but showing a Toast message like the following.

public MyActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        Toast.makeText(this, "Some message", Toast.LENGTH_LONG).show();
        finish(); // Finish the activity here. 
    }
}

I want to write a unit test using Robolectric to verify the Toast content. I tried the following.

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {

    @Before
    public void setup() {
        Robolectric.buildActivity(MyActivity.class).create();
    }

    @Test
    public void testToast() {
        assertTrue(ShadowToast.showedCustomToast("Some message", R.string.some_message));
        assertThat(ShadowToast.getTextOfLatestToast().toString(), equalTo("Some message"));
    }
}

Looks like none of them are working. Any ideas?

Thanks in advance!


Solution

  • I could manage to get a workaround for my problem and ended up having a custom layout for showing toast messages and testing the content of the toast using Robolectric as follows. I have added a GitHub project here with a working example.

    public class MainActivityTest {
    
        @Before
        public void setup() {
            Robolectric.buildActivity(MainActivity.class).create();
        }
    
        @Test
        public void testToastMessage() {
            ToastLayoutBinding binding = DataBindingUtil.getBinding(ShadowToast.getLatestToast().getView());
            binding.executePendingBindings();
    
            assertEquals(binding.toastMsg.getContext().getString(R.string.some_toast),
                    binding.toastMsg.getText());
        }
    
        @Test
        public void testToastDuration() {
            assertEquals(Toast.LENGTH_LONG, ShadowToast.getLatestToast().getDuration());
        }
    }
    

    The layout for the custom toast is simply as follows.

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
        <data>
    
            <import type="android.text.TextUtils" />
    
            <variable
                name="msg"
                type="String" />
        </data>
    
        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:elevation="4dp">
    
            <TextView
                android:id="@+id/toast_msg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:fontFamily="sans-serif"
                android:gravity="center"
                android:text="@{TextUtils.isEmpty(msg) ? @string/no_msg : msg}" />
        </FrameLayout>
    </layout>
    

    And the function for displaying the toast message is as follows.

    /**
    * Display a custom toast with some message provided as a function parameter
    *
    * @param activity An {@link Activity} where the toast message will be shown
    * @param msg      a {@link String} that holds the message to be shown as a Toast
    * @throws IllegalArgumentException if a null activity is passed to the function
    * @apiNote If a null String is passed as a #msg parameter, then this function shows a default text (no_toast)
    */
    public static void showToast(Activity activity, String msg) {
    
        if (activity == null) {
            throw new IllegalArgumentException("The passed in activity cannot be null");
        }
    
        if (msg == null) {
            msg = activity.getString(R.string.no_msg);
        }
    
        ToastLayoutBinding toastLayout = DataBindingUtil.inflate(
                activity.getLayoutInflater(), R.layout.toast_layout, null, false);
        toastLayout.setMsg(msg); // Set the toast message here
    
        Toast toast = new Toast(activity);
        toast.setView(toastLayout.getRoot());
        toast.setGravity(Gravity.TOP, 0, 200);
        toast.setDuration(Toast.LENGTH_LONG);
        toast.show();
    }
    

    I have used data-binding, however, this should also work with other layout inflation approaches.

    I hope that helps other developers having the same problem.