Closed nuxzero closed 7 years ago
My solution for this problem. I hope this will help some people that got problem about mock ViewModel for test fragment.
AFragmentTest.kt
@RunWith(AndroidJUnit4::class)
class AFragmentTest {
@Rule
@JvmField
val activityRule = ActivityTestRule(TestSingleFragmentActivity::class.java, true, true)
@Rule
@JvmField
val taskRule = TaskExecutorWithIdlingResourceRule()
private lateinit var fragment: AFragment
private lateinit var viewModel: AViewModel
private val aData = MutableLiveData<Resource<A>>()
private lateinit var fragmentBindingAdapters: FragmentBindingAdapters
private lateinit var fragmentDataBindingComponent: FragmentDataBindingComponent
@Before
fun setUp() {
fragment = AFragment()
viewModel = mock(AViewModel::class.java)
fragment.viewModel = viewModel
// Mock DataBindingComponent and set to fragment
fragmentBindingAdapters = mock(FragmentBindingAdapters::class.java)
fragmentDataBindingComponent = mock(FragmentDataBindingComponent::class.java)
`when`(fragmentDataBindingComponent.fragmentBindingAdapters).thenReturn(fragmentBindingAdapters)
fragment.dataBindingComponent = fragmentDataBindingComponent
`when`(viewModel.aData).thenReturn(aData)
activityRule.activity.setFragment(fragment)
}
...
AFragment.kt
class AFragment : Fragment() {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
var viewModel: AViewModel? = null
lateinit var binding: AFragmentBinding
// DataBindingComponent required for UI test
var dataBindingComponent: DataBindingComponent = FragmentDataBindingComponent(this)
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Also need to set dataBindingComponent to binding object.
binding = DataBindingUtil.inflate(inflater, R.layout.a_fragment, container, false, dataBindingComponent)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// This protected fragment create new viewModel after create mock.
// If mock already created, no need to set viewModel.
if(viewModel == null) {
viewModel = ViewModelProviders.of(this, viewModelFactory).get(AViewModel::class.java)
}
}
...
This works ofc, but this is not the proper solution I would say, you are exposing your ViewModel
, it should be private, and if possible not set
after fragment instantiation.
i am using roboelectric for the same ,but still could not mock viewmodel
@SrishtiRoy have you tried mocking the ViewModel factory and return your own viewmodel instance?
Joaquim Ley
🌎: joaquimley.com http://www.joaquimley.com/ 📍Copenhagen, Denmark [image: Twitter Joaquim Ley] https://twitter.com/joaquimley https://github.com/joaquimley https://twitch.tv/joaquimley https://medium.com/@JoaquimLey
On Wed, Jan 30, 2019 at 5:04 AM SrishtiRoy notifications@github.com wrote:
i am using roboelectric for the same ,but still could not mock viewmodel
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/googlesamples/android-architecture-components/issues/196#issuecomment-458804027, or mute the thread https://github.com/notifications/unsubscribe-auth/AGmh_LuvvDitPpQ3dSO8FIXUY6L2WUPnks5vIRm-gaJpZM4PyBd5 .
@JoaquimLey that worked ,just want to know does Data Binding in xml does not work with roboelectric, i am not able to access value that are set in viewmodel as observable field [@Test public void clickLogInButton_CallsViewModel() { Assert.assertNotNull(fragment);
Button fav = fragment.getView().findViewById(R.id.fav);
assertTrue(fav.getVisibility() == View.VISIBLE);
assertEquals(fav.getText(),"Unfavorite");
fav.performClick();
verify(mockViewModel).setFavorite();
}](url)
Sorry, can't help you with that, never used robolectric with databinding :\
The reason the mock object does not work is because it was replaced when the dependency was injected,here is my solution to mock the ViewModel
, in your test class add:
val viewModel = mock(MyViewModel::class.java)
fragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
/*
In general, dependencies are injected in the onAttach method.
So, we can replace the injected ViewModel with a mock ViewModel after the method called.
The method execution order in fragment: onAttach -> onCreate -> onCreateView -> onFragmentViewCreated
*/
override fun onFragmentViewCreated(
fm: FragmentManager,
f: Fragment,
v: View,
savedInstanceState: Bundle?
) {
(f as SignInFragment).viewModel = viewModel // replace ViewModel with the mock one
}
}, false)
From GithubBrowserSample project. I got problem when I mock
ViewModel
to use to testFragment
but it look like mock not working. It still callRepository
inViewModel
.Here is code in test class
Inside fragment
Do I need to use the
FragmentDataBindingComponent
from the sample?