Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recyclerview 0 children, can't test viewholder click #3747

Open
JustinTullgren opened this issue Feb 9, 2018 · 4 comments

Comments

@JustinTullgren
Copy link

commented Feb 9, 2018

Description

Calling notifyDataSetChanged() and calling ActivityController.visible() does not update the recyclerview childcount or returns null for recycler.findViewHolderForAdapterPosition(0)

I tried follow steps in this Stackoverflow post which mentioned the visible method. The other results didn't work either.

The activity works and loads fine.

I am not sure its a bug or just a misunderstanding on my part. I tried looking at the github samples and articles on line but they all had similar answers (draw the recycler manually) or were very shallow.

Steps to Reproduce: Code below(modified to not show domain details)

Activity

class TheActivity : BaseActivity<IView, IPresenter>(), IView {

	private val theAdapter = TheAdapter { presenter.select(it) }
	@Inject lateinit var presenter: IPresenter

	override fun onInject() {
		component?.inject(this)
	}

	override fun getIPresenter(): IPresenter = presenter
	override fun getIView(): IView = this

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_example)
		with(activity_example_recyclerview) {
			adapter = theAdapter
			layoutManager = LinearLayoutManager(this@TheActivity)
		}

	}

	override fun setItems(items: List<String>) {
		theAdapter.setItems(items)
	}

	private class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

	private class TheAdapter(private val itemClick: (String) -> Unit) : RecyclerView.Adapter<ViewHolder>() {
		private var list = listOf<String>()
		fun setItems(list: List<String>) {
			this.list = list
			notifyDataSetChanged()
		}
		override fun onBindViewHolder(holder: ViewHolder, position: Int) {
			val item = list[position]
			holder.itemView.setOnClickListener { itemClick(item) }
			val text = holder.itemView as TextView
			text.text = item
		}

		override fun getItemCount(): Int = list.size

		override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(TextView(parent.context))
	}
}

Test

@RunWith(RobolectricTestRunner::class)
class TheActivityUnitTest {
	private lateinit var subject:TheActivity
	private val controller = Robolectric.buildActivity(TheActivity::class.java)
	private val mockExistingItems = listOf("foo", "bar", "baz")
	private val mockPresenter = mock<IPresenter>()
	private val mockComponent = mock<AppCompatActivityComponent> {
		on { inject(any<TheActivity>()) }.doAnswer {
			with(it.arguments[0] as TheActivity) {
				presenter = mockPresenter
			}
			return@doAnswer null
		}
	}

	@Before
	fun setup() {
		subject = controller.get()
		subject.component = mockComponent
	}

	@Test
	fun selectItemFromList() {
		controller.create().start().visible()
		subject.setItems(mockExistingItems) // Invokes notifyDataSetChanged()
		controller.visible()
		subject.findViewById<RecyclerView>(R.id.activity_example_recyclerview)
				.findViewHolderForItemId(0)
				// THIS WILL FAIL as findViewHolderForItemId returns null
				.itemView
				.performClick()
		// OR Alternatively 
		/*
		subject.findViewById<RecyclerView>(R.id.activity_example_recyclerview)
				.findViewHolderForAdapterPosition(0)
				.itemView
				.performClick()
		 */
		
		// OR Alternatively 
		/*
		subject.findViewById<RecyclerView>(R.id.activity_example_recyclerview)
				.getChildAt(0)
				.performClick()
		 */
		verify(mockPresenter).select(mockExistingItems[0])
	}
}

Robolectric & Android Version

Robolectric: 3.6.1
Android compileSdkVersion: 26,
android minSdkVersion : 21,
android targetSdkVersion : 26

@JustinTullgren

This comment has been minimized.

Copy link
Author

commented Feb 9, 2018

Doing the measure manually fixed the issue. It would still be nice if this was not required.

val recycler = subject.findViewById<RecyclerView>(R.id.activity_example_recyclerview)
recycler.measure(0,0)
recycler.layout(0,0,100,1000)
@amrfarid140

This comment has been minimized.

Copy link

commented Jan 31, 2019

Getting the same issue on Robolectric 4.1, @JustinTullgren suggested solution works.

@brettchabot

This comment has been minimized.

Copy link
Contributor

commented Jan 31, 2019

I'm hoping this issue will be fixed by the fix for #4153 which I've been working on.

Robolectric currently performs layout assuming the window is zero-size, which leads to a bunch of inconsistencies when sizing TextViews, RecyclerViews etc.

@lupsyn

This comment has been minimized.

Copy link

commented Feb 19, 2019

Same issue on Roboelectric 4.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.