Marc Freeman
Marc Freeman

Reputation: 753

Android: Mock a Context for Fragment unit testing

I have a fragment that uses the delegation pattern when a button is clicked.

class FutureMeetingEventViewFragment @Inject constructor(): Fragment() {

    @Inject
    lateinit var bundleUtilityModule: BundleUtilityModule
    lateinit var parcelableMeetingEvent: ParcelableMeetingEvent

    private var delegate: IEventDetailsDelegate? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)

        if(context is IEventDetailsDelegate) {
            delegate = context
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)

        val binding: ViewDataBinding = DataBindingUtil.inflate<ViewDataBinding>(inflater, R.layout.layout_future_meeting_event_fragment, container,false)
        DaggerUtilityModuleComponent.create().inject(this)
        this.parcelableMeetingEvent = this.bundleUtilityModule.getTypeFromBundle(BundleData.MEETING_EVENT_DATA.name, arguments)

        binding.setVariable(BR.meetingEvent, this.parcelableMeetingEvent)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        this.setCTAClickEvent()
    }

    private fun setCTAClickEvent() {
        future_event_card.setOnClickListener {
            delegate?.onEventClicked(this, this.parcelableMeetingEvent)
        }
    }
}

A problem I can see in the unit test is that when I click the button, because the IEventDetailsDelegate field will be null, the test will always fail. My unit test so far is simply testing if the correct data is displayed on the view:


@RunWith(AndroidJUnit4::class)
class GivenAFutureMeetingEventFragmentIsDisplayed  {

    private var fragmentId: Int = 0

    private lateinit var fragmentArgs: Bundle
    private lateinit var scenario: FragmentScenario<FutureMeetingEventViewFragment>

    @Before
    fun setup() {
        fragmentId = FutureMeetingEventViewFragment().id

        fragmentArgs = Bundle()
        fragmentArgs.apply {
            putParcelable(BundleData.MEETING_EVENT_DATA.name, ParcelableMeetingEvent(
                "Test Description",
                "Test Summary",
                "12344556",
                DateTime("2020-03-14T17:57:59+00:00"),
                DateTime("2020-03-14T18:57:59+00:00"),
                "Somewhere across the universe"
            ))
        }

        scenario = launchFragmentInContainer<FutureMeetingEventViewFragment>(
            fragmentArgs,
            fragmentId
        )
    }

    @Test
    fun thenACorrectlyMappedMeetingEventShouldBePassedToTheFragment() {
        onView(withId(R.id.spec_future_meeting_event_start_day)).check(matches(withText("Saturday")))
        onView(withId(R.id.spec_future_meeting_event_start_time)).check(matches(withText("5:57:59 PM")))
        onView(withId(R.id.spec_future_meeting_event_end_time)).check(matches(withText("6:57:59 PM")))
        onView(withId(R.id.spec_future_meeting_event_summary)).check(matches(withText("Test Summary")))
        onView(withId(R.id.spec_future_meeting_event_description)).check(matches(withText("Test Description")))
    }

    @Test
    fun thenTheContainerShouldBeClickable() {
        onView(withId(R.id.future_event_card)).check(matches(isClickable()))
    }
}

I guess there's two questions in this post:

  1. Can I mock out a context that implements the IEventDetailsDelegate and assign it to my mock fragment?
  2. Should I test the event on an actual activity that implements the interface?

Upvotes: 2

Views: 1203

Answers (0)

Related Questions