Back to Repositories

Testing OnFocusChange Annotation Processing in ButterKnife

This test suite validates the OnFocusChange annotation functionality in ButterKnife, ensuring proper view binding and focus change event handling. It verifies the generation of appropriate binding classes and proper lifecycle management for focus change listeners.

Test Coverage Overview

The test suite provides comprehensive coverage of the @OnFocusChange annotation functionality.

Key areas tested include:
  • View binding generation with correct focus change listener implementation
  • Proper method binding for focus change callbacks
  • Cleanup and unbinding of focus change listeners
  • View reference management and null safety checks

Implementation Analysis

The testing approach uses compile-time verification to ensure correct code generation for view bindings. It leverages the JavaFileObjects utility for source code manipulation and verification, while implementing compile-time assertion patterns to validate the generated binding classes.

The test specifically verifies:
  • Annotation processing accuracy
  • Generated binding class structure
  • Focus change listener implementation
  • Proper cleanup in unbind() method

Technical Details

Testing tools and configuration:
  • JUnit test framework for test execution
  • ButterKnifeProcessor for annotation processing
  • Google Testing Compile utilities for source validation
  • Truth assertion framework for compile-time verification
  • Custom compiler options to suppress processing warnings

Best Practices Demonstrated

The test exhibits several testing best practices for annotation processing verification:

  • Isolated test cases with clear input/output validation
  • Comprehensive verification of generated code structure
  • Proper cleanup and resource management testing
  • Clear separation of test resources and assertions
  • Effective use of compile-time testing utilities

jakewharton/butterknife

butterknife-runtime/src/test/java/butterknife/OnFocusChangeTest.java

            
package butterknife;

import butterknife.compiler.ButterKnifeProcessor;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
import org.junit.Test;

import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;

public class OnFocusChangeTest {
  @Test public void focusChange() {
    JavaFileObject source = JavaFileObjects.forSourceString("test.Test", ""
        + "package test;\n"
        + "import butterknife.OnFocusChange;\n"
        + "public class Test {\n"
        + "  @OnFocusChange(1) void doStuff() {}\n"
        + "}"
    );

    JavaFileObject bindingSource = JavaFileObjects.forSourceString("test/Test_ViewBinding", ""
        + "package test;\n"
        + "import android.view.View;\n"
        + "import androidx.annotation.CallSuper;\n"
        + "import androidx.annotation.UiThread;\n"
        + "import butterknife.Unbinder;\n"
        + "import butterknife.internal.Utils;\n"
        + "import java.lang.IllegalStateException;\n"
        + "import java.lang.Override;\n"
        + "public class Test_ViewBinding implements Unbinder {\n"
        + "  private Test target;\n"
        + "  private View view1;\n"
        + "  @UiThread\n"
        + "  public Test_ViewBinding(final Test target, View source) {\n"
        + "    this.target = target;\n"
        + "    View view;\n"
        + "    view = Utils.findRequiredView(source, 1, \"method 'doStuff'\");\n"
        + "    view1 = view;\n"
        + "    view.setOnFocusChangeListener(new View.OnFocusChangeListener() {\n"
        + "      @Override\n"
        + "      public void onFocusChange(View p0, boolean p1) {\n"
        + "        target.doStuff();\n"
        + "      }\n"
        + "    });\n"
        + "  }\n"
        + "  @Override\n"
        + "  @CallSuper\n"
        + "  public void unbind() {\n"
        + "    if (target == null) throw new IllegalStateException(\"Bindings already cleared.\");\n"
        + "    target = null;\n"
        + "    view1.setOnFocusChangeListener(null);\n"
        + "    view1 = null;\n"
        + "  }\n"
        + "}"
    );

    assertAbout(javaSource()).that(source)
        .withCompilerOptions("-Xlint:-processing")
        .processedWith(new ButterKnifeProcessor())
        .compilesWithoutWarnings()
        .and()
        .generatesSources(bindingSource);
  }
}