Event Handler Testing
This document provides a quick example of how to write tests for your event handlers. You should be familiar with TestSpecs before diving into this topic.
Prerequisitesβ
The package is shipped as a separate module. It is available via maven as
com.facebook.litho:litho-testing
. To include it in your gradle build, add this
line to your dependencies
block:
testImplementation 'com.facebook.litho:litho-testing:0.50.0'
What to test forβ
We are going to work with this spec in our example:
@LayoutSpec
public class LearningStateComponentSpec {
@PropDefault static final boolean canClick = true;
@OnCreateInitialState
static void onCreateInitialState(
ComponentContext c,
StateValue<Integer> count) {
count.set(0);
}
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop(optional = true) boolean canClick,
@State Integer count) {
return Text.create(c)
.text("Clicked " + count + " times.")
.textSizeDip(50)
.clickHandler(canClick ? LearningStateComponent.onClick(c) : null)
.backgroundRes(android.R.color.holo_blue_light)
.alignSelf(STRETCH)
.paddingDip(BOTTOM, 20)
.paddingDip(TOP, 40)
.build();
}
@OnUpdateState
static void incrementClickCount(StateValue<Integer> count) {
count.set(count.get() + 1);
}
@OnEvent(ClickEvent.class)
static void onClick(ComponentContext c) {
LearningStateComponent.incrementClickCount(c);
}
}
When testing event handlers, it is important to remember what you actually want to validate in your unit test. You may be getting this inkling to ensure that a click event you issue triggers the callback you pass in as your prop. When you do this, you are actually testing the framework. This is not what you want to spend your time on. While writing high-level end-to-end tests ensuring that your touch events have the right effects, this is not what you should concern yourself with for unit tests.
Testing handler presenceβ
Instead, let's focus on the actual logic that we have in our spec. Whether or
not we have a click handler depends on the prop canClick
. It is very common
for handlers to be set conditionally based on a prop. In our test, we are going
to limit ourselves to checking if a handler is set or not. For that we can use
the TestSpec matchers we have learned about before.
@RunWith(LithoTestRunner.class)
public class LearningStateComponentSpecTest {
@Rule public LithoViewRule mLithoViewRule = new LithoViewRule();
@Test
public void testComponentOnClick() {
final ComponentContext c = mLithoViewRule.getContext();
final Component component = LearningStateComponent.create(
c)
.canClick(true)
.build();
LegacyLithoAssertions.assertThat(c, component).has(
SubComponentExtractor.subComponentWith(
c,
TestText.matcher(c)
.clickHandler(IsNull.<EventHandler<ClickEvent>>notNullValue(null)).build())
);
}
@Test
public void testNoComponentOnClick() {
final ComponentContext c = mLithoViewRule.getContext();
final Component component = LearningStateComponent.create(
c)
.canClick(false)
.build();
LegacyLithoAssertions.assertThat(c, component).has(
SubComponentExtractor.subComponentWith(
c,
TestText.matcher(c)
.clickHandler(IsNull.<EventHandler<ClickEvent>>nullValue(null)).build())
);
}
}
As you can see here, we can match against a click handler just like any other
prop set on a sub-component. Matching against a specific instance of an
EventHandler
is currently not supported.
Nextβ
Either head back to the testing overview or continue with the next section, Espresso.