Litho: InstrumentalTests: Unresolved reference: create

Created on 22 May 2018  路  8Comments  路  Source: facebook/litho

Version

ext {
    screenshotTestVersion = '0.6.0'
    kotlinVersion = '1.2.41'
    litho = '0.15.0'
}

Issues and Steps to Reproduce

I create test according to https://github.com/facebook/screenshot-tests-for-android/blob/master/sample/src/androidTest/java/com/facebook/testing/screenshot/sample/ExampleScreenshotTest.java

e: ../app/src/androidTest/java/kt/test/instrumental/screenshots/specs/TestLandingSpec.kt: (26, 33): Unresolved reference: create
:kt-app:compileDevStagingAndroidTestKotlin FAILED

Link to Code

Spec

@LayoutSpec
object LandingSpec {

    @OnCreateLayout
    fun onCreateLayout(context: ComponentContext) : Component {
        return Row
                .create(context)
                .backgroundRes(R.color.color_white)
                .widthPercent(100f)
                .heightPercent(100f)
                .child(
                        Image.create(context)
                                .drawableRes(R.drawable.ic_splash_screen)
                )
                .justifyContent(YogaJustify.CENTER)
                .alignItems(YogaAlign.CENTER)
                .build()
    }
}

Test

class TestLandingSpec {

    @Test
    fun testLanding() {
        val context = InstrumentationRegistry.getInstrumentation().targetContext

        val li = LayoutInflater.from(context)

        val lithoView = li.inflate(R.layout.test_litho_view, null, false) as LithoView

        val component = Landing.create(lithoView.componentContext).build()

        lithoView.setComponent(component)

        ViewHelpers.setupView(lithoView).setExactWidthDp(360).setExactHeightDp(640).layout()
        Screenshot.snap(lithoView).record()
    }
}

But it works when:

  • i create component without generated code
    val component = LandingSpec.onCreateLayout(lithoView.componentContext)
  • i create screenshot of whole activity with this component

Most helpful comment

Okay I managed to find a solution for your case! There seems to be a bug somewhere (not sure where though).

Solution is consisted of two parts:

1) You need to add kaptAndroidTest 'com.facebook.litho:litho-processor:0.15.0' so that it can invoke the kapt AP for androidTest related tasks as well!

2) Regarding the issue I mentioned above, it seems that generated files are not properly copied over to kapt generated sources folder.

Add the following on your app's module build.gradle:

task copyTestClasses(type: Copy) {
  from "build/generated/source/kapt/debug"
  into "build/generated/source/kapt/debugAndroidTest"
}

afterEvaluate {
  compileDebugAndroidTestKotlin.dependsOn copyTestClasses
}

What this does, is before running compileDebugAndroidTestKotlin task it copies the generated .java files over to the androidTest folder, in order for the test to be able to find those classes on runtime.

Edit 1: Seems that this won't work properly when combined with clean, so we modify the copyTestClasses task in order to force connectedDebugAndroidTestKotlin to wait for the classes to be copied, before executing.

task copyTestClasses(type: Copy) {
    from "build/generated/source/kapt/debug"
    into "build/generated/source/kapt/debugAndroidTest"
    mustRunAfter "kaptDebugAndroidTestKotlin"
}

All 8 comments

Hey @just-kip can you post the generated code for Landing ?

~My guess is though that you should probably write something like Landing.INSTANCE.create(...)~ ~since you are using Kotlin's object to generate your Spec :)~

~Let me know if this works for you!~

Edit #1: Sorry the previous reply was referring to the generated code. Your Component should still be accessible with Landing.create(..).

Waiting for you to post the generated code :)

/**
 *
 * @see kt.ui.landing.LandingSpec
 */
@Metadata(
    mv = {
        1,
        1,
        10
    },
    bv = {
        1,
        0,
        2
    },
    k = 1,
    d1 = "\u0000\u0018\n"
            + "\u0002\u0018\u0002\n"
            + "\u0002\u0010\u0000\n"
            + "\u0002\b\u0002\n"
            + "\u0002\u0018\u0002\n"
            + "\u0000\n"
            + "\u0002\u0018\u0002\n"
            + "\u0000\b脟\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002垄\u0006\u0002\u0010\u0002J\u0010\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u0006H\u0007篓\u0006\u0007",
    d2 = {
        "Lkt/ui/landing/LandingSpec;",
        "",
        "()V",
        "onCreateLayout",
        "Lcom/facebook/litho/Component;",
        "context",
        "Lcom/facebook/litho/ComponentContext;",
        "kt-app_devStaging"
    }
)
public final class Landing extends Component {
  private Landing() {
    super("Landing");
  }

  @Override
  public boolean isEquivalentTo(Component other) {
    if (this == other) {
      return true;
    }
    if (other == null || getClass() != other.getClass()) {
      return false;
    }
    Landing landingRef = (Landing) other;
    if (this.getId() == landingRef.getId()) {
      return true;
    }
    return true;
  }

  @Override
  protected Component onCreateLayout(ComponentContext context) {
    Component _result = (Component) LandingSpec.INSTANCE.onCreateLayout(
      (ComponentContext) context);
    return _result;
  }

  public static Builder create(ComponentContext context) {
    return create(context, 0, 0);
  }

  public static Builder create(ComponentContext context, int defStyleAttr, int defStyleRes) {
    final Builder builder = new Builder();
    Landing instance = new Landing();
    builder.init(context, defStyleAttr, defStyleRes, instance);
    return builder;
  }

  public static class Builder extends Component.Builder<Builder> {
    Landing mLanding;

    ComponentContext mContext;

    private void init(ComponentContext context, int defStyleAttr, int defStyleRes,
        Landing landingRef) {
      super.init(context, defStyleAttr, defStyleRes, landingRef);
      mLanding = landingRef;
      mContext = context;
    }

    @Override
    public Builder getThis() {
      return this;
    }

    @Override
    public Landing build() {
      Landing landingRef = mLanding;
      release();
      return landingRef;
    }

    @Override
    protected void release() {
      super.release();
      mLanding = null;
      mContext = null;
    }
  }
}

@just-kip That seems to be correct, do you have a reproducing repo where we can check it? :)

i created new project with this issue
https://github.com/just-kip/litho-pg

Okay I managed to find a solution for your case! There seems to be a bug somewhere (not sure where though).

Solution is consisted of two parts:

1) You need to add kaptAndroidTest 'com.facebook.litho:litho-processor:0.15.0' so that it can invoke the kapt AP for androidTest related tasks as well!

2) Regarding the issue I mentioned above, it seems that generated files are not properly copied over to kapt generated sources folder.

Add the following on your app's module build.gradle:

task copyTestClasses(type: Copy) {
  from "build/generated/source/kapt/debug"
  into "build/generated/source/kapt/debugAndroidTest"
}

afterEvaluate {
  compileDebugAndroidTestKotlin.dependsOn copyTestClasses
}

What this does, is before running compileDebugAndroidTestKotlin task it copies the generated .java files over to the androidTest folder, in order for the test to be able to find those classes on runtime.

Edit 1: Seems that this won't work properly when combined with clean, so we modify the copyTestClasses task in order to force connectedDebugAndroidTestKotlin to wait for the classes to be copied, before executing.

task copyTestClasses(type: Copy) {
    from "build/generated/source/kapt/debug"
    into "build/generated/source/kapt/debugAndroidTest"
    mustRunAfter "kaptDebugAndroidTestKotlin"
}

Thank u, it works.
In my "big" project i also added

 androidTestCompileOnly'com.facebook.litho:litho-annotations:0.15.0'

Without it project crashes failing to find com/facebook/litho/annotations/Prop

Another question.

After ./gradlew clean
first execution of ./gradlew connectedDebugAndroidTest
fails with /LandingTest.kt: (20, 33): Unresolved reference: create. And kapt files does not appear in debugAndroidTest folder. Second execution ./gradlew connectedDebugAndroidTest works well.

Yeap, if you are using annotations as well it needs to be added as an androidTest dependency. Will try to reproduce the second one in your repo and see if it happens there as well!

Hey @just-kip seems that indeed ./gradlew clean connectedDebugAndroidTest does not work properly. I have modified the copyTestClasses task in order to make it run always after kaptDebugAndroidTestKotlin and since compileDebugAndroidTestKotlin depends on it, it waits until it copies those classes over, before running.

Here is the modified task:

task copyTestClasses(type: Copy) {
    from "build/generated/source/kapt/debug"
    into "build/generated/source/kapt/debugAndroidTest"
    mustRunAfter "kaptDebugAndroidTestKotlin"
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Junaid-Sakib picture Junaid-Sakib  路  7Comments

harliedharma picture harliedharma  路  3Comments

kosiarska picture kosiarska  路  7Comments

MichaelRocks picture MichaelRocks  路  7Comments

Zeyad-37 picture Zeyad-37  路  8Comments