使用@Test注释和ActivityTestRule与Robotium(Espresso-way)

时间:2016-08-05 07:31:22

标签: java android android-studio junit robotium

我想知道如何在JUnit中将我的测试标记为@Test,因为到目前为止我必须使用'测试...'名称。这意味着:

@Test
private void creatingAcc(){

而不是

private void testCreatingAcc(){

提前致谢

1 个答案:

答案 0 :(得分:0)

是的,你可以做到。请按照以下步骤实现:

  1. 转到您的应用build.gradle文件并添加如下所示的测试依赖关系:

        androidTestCompile 'com.android.support.test:runner:0.4.1'
        androidTestCompile 'com.android.support.test:rules:0.4.1'
        androidTestCompile 'com.android.support:support-annotations:24.1.1'
        compile 'com.jayway.android.robotium:robotium-solo:5.6.1'
    

    现在你的build.gradle应该是这样的:

    apply plugin: 'com.android.application'
    
    android {
            compileSdkVersion 24
            buildToolsVersion "24.0.1"
    
    defaultConfig {
          applicationId "com.example.piotr.myapplication"
          minSdkVersion 15
          targetSdkVersion 21
          versionCode 1
          versionName "1.0"
          testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
      }
    
       buildTypes {
          release {
              minifyEnabled false
              proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
          }
    
       }
    }
    
    dependencies {
           compile fileTree(dir: 'libs', include: ['*.jar'])
           testCompile 'junit:junit:4.12'
           compile 'com.android.support:appcompat-v7:24.1.1'
           compile 'com.android.support:design:24.1.1'
           androidTestCompile 'com.android.support.test:runner:0.4.1'
           androidTestCompile 'com.android.support.test:rules:0.4.1'
           androidTestCompile 'com.android.support:support-annotations:24.1.1'
           compile 'com.jayway.android.robotium:robotium-solo:5.6.1'    
    }
    
  2. 然后你需要转到你的androidTest目录并创建Java类并命名为例如MyActivityTestRule

  3. 在此文件中输入以下代码:

    @Beta
    public class MyActivityTestRule<T extends Activity> extends UiThreadTestRule {
    
    private static final String TAG = "ActivityInstrumentationRule";
    
    private final Class<T> mActivityClass;
    
    public Instrumentation getInstrumentation() {
        return mInstrumentation;
    }
    
    private Instrumentation mInstrumentation;
    
    private boolean mInitialTouchMode = false;
    
    private boolean mLaunchActivity = false;
    
    private T mActivity;
    
    /**
     * Similar to {@link #MyActivityTestRule(Class, boolean, boolean)} but with "touch mode" disabled.
     *
     * @param activityClass    The activity under test. This must be a class in the instrumentation
     *                         targetPackage specified in the AndroidManifest.xml
     * @see MyActivityTestRule#MyActivityTestRule(Class, boolean, boolean)
     */
    public MyActivityTestRule(Class<T> activityClass) {
        this(activityClass, false);
    }
    
    /**
     * Similar to {@link #MyActivityTestRule(Class, boolean, boolean)} but defaults to launch the
     * activity under test once per
     * <a href="http://junit.org/javadoc/latest/org/junit/Test.html"><code>Test</code></a> method.
     * It is launched before the first
     * <a href="http://junit.sourceforge.net/javadoc/org/junit/Before.html"><code>Before</code></a>
     * method, and terminated after the last
     * <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
     * method.
     *
     * @param activityClass    The activity under test. This must be a class in the instrumentation
     *                         targetPackage specified in the AndroidManifest.xml
     * @param initialTouchMode true if the Activity should be placed into "touch mode" when started
     * @see MyActivityTestRule#MyActivityTestRule(Class, boolean, boolean)
     */
    public MyActivityTestRule(Class<T> activityClass, boolean initialTouchMode) {
        this(activityClass, initialTouchMode, true);
    }
    
    /**
     * Creates an {@link MyActivityTestRule} for the Activity under test.
     *
     * @param activityClass    The activity under test. This must be a class in the instrumentation
     *                         targetPackage specified in the AndroidManifest.xml
     * @param initialTouchMode true if the Activity should be placed into "touch mode" when started
     * @param launchActivity   true if the Activity should be launched once per
     *                         <a href="http://junit.org/javadoc/latest/org/junit/Test.html">
     *                         <code>Test</code></a> method. It will be launched before the first
     *                         <a href="http://junit.sourceforge.net/javadoc/org/junit/Before.html">
     *                         <code>Before</code></a> method, and terminated after the last
     *                         <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html">
     *                         <code>After</code></a> method.
     */
    public MyActivityTestRule(Class<T> activityClass, boolean initialTouchMode,
                              boolean launchActivity) {
        mActivityClass = activityClass;
        mInitialTouchMode = initialTouchMode;
        mLaunchActivity = launchActivity;
        mInstrumentation = InstrumentationRegistry.getInstrumentation();
    }
    
    /**
     * Override this method to set up Intent as if supplied to
     * {@link android.content.Context#startActivity}.
     * <p>
     * The default Intent (if this method returns null or is not overwritten) is:
     * action = {@link Intent#ACTION_MAIN}
     * flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
     * All other intent fields are null or empty.
     *
     * @return The Intent as if supplied to {@link android.content.Context#startActivity}.
     */
    protected Intent getActivityIntent() {
        return new Intent(Intent.ACTION_MAIN);
    }
    
    /**
     * Override this method to execute any code that should run before your {@link Activity} is
     * created and launched.
     * This method is called before each test method, including any method annotated with
     * <a href="http://junit.sourceforge.net/javadoc/org/junit/Before.html"><code>Before</code></a>.
     */
    protected void beforeActivityLaunched() {
        // empty by default
    }
    
    /**
     * Override this method to execute any code that should run after your {@link Activity} is
     * launched, but before any test code is run including any method annotated with
     * <a href="http://junit.sourceforge.net/javadoc/org/junit/Before.html"><code>Before</code></a>.
     * <p>
     * Prefer
     * <a href="http://junit.sourceforge.net/javadoc/org/junit/Before.html"><code>Before</code></a>
     * over this method. This method should usually not be overwritten directly in tests and only be
     * used by subclasses of MyActivityTestRule to get notified when the activity is created and
     * visible but test runs.
     */
    protected void afterActivityLaunched() {
        // empty by default
    }
    
    /**
     * Override this method to execute any code that should run after your {@link Activity} is
     * finished.
     * This method is called after each test method, including any method annotated with
     * <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>.
     */
    protected void afterActivityFinished() {
        // empty by default
    }
    
    /**
     * @return The activity under test.
     */
    public T getActivity() {
        if (mActivity == null) {
            Log.w(TAG, "Activity wasn't created yet");
        }
        return mActivity;
    }
    
    @Override
    public Statement apply(final Statement base, Description description) {
        return new ActivityStatement(super.apply(base, description));
    }
    
    /**
     * Launches the Activity under test.
     * <p>
     * Don't call this method directly, unless you explicitly requested not to launch the Activity
     * manually using the launchActivity flag in
     * {@link MyActivityTestRule#MyActivityTestRule(Class, boolean, boolean)}.
     * <p>
     * Usage:
     * <pre>
     *    &#064;Test
     *    public void customIntentToStartActivity() {
     *        Intent intent = new Intent(Intent.ACTION_PICK);
     *        mActivity = mActivityRule.launchActivity(intent);
     *    }
     * </pre>
     * @param startIntent The Intent that will be used to start the Activity under test. If
     *                    {@code startIntent} is null, the Intent returned by
     *                    {@link MyActivityTestRule#getActivityIntent()} is used.
     * @return the Activity launched by this rule.
     * @see MyActivityTestRule#getActivityIntent()
     */
    public T launchActivity(@Nullable Intent startIntent) {
        // set initial touch mode
        mInstrumentation.setInTouchMode(mInitialTouchMode);
    
        final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
        // inject custom intent, if provided
        if (null == startIntent) {
            startIntent = getActivityIntent();
            if (null == startIntent) {
                Log.w(TAG, "getActivityIntent() returned null using default: " +
                        "Intent(Intent.ACTION_MAIN)");
                startIntent = new Intent(Intent.ACTION_MAIN);
            }
        }
        startIntent.setClassName(targetPackage, mActivityClass.getName());
        startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Log.d(TAG, String.format("Launching activity %s",
                mActivityClass.getName()));
    
        beforeActivityLaunched();
        // The following cast is correct because the activity we're creating is of the same type as
        // the one passed in
        mActivity = mActivityClass.cast(mInstrumentation.startActivitySync(startIntent));
    
        mInstrumentation.waitForIdleSync();
    
        afterActivityLaunched();
        return mActivity;
    }
    
    // Visible for testing
    void setInstrumentation(Instrumentation instrumentation) {
        mInstrumentation = checkNotNull(instrumentation, "instrumentation cannot be null!");
    }
    
    void finishActivity() {
        if (mActivity != null) {
            mActivity.finish();
            mActivity = null;
        }
    }
    
    /**
     * <a href="http://junit.org/apidocs/org/junit/runners/model/Statement.html">
     * <code>Statement</code></a> that finishes the activity after the test was executed
     */
    private class ActivityStatement extends Statement {
    
        private final Statement mBase;
    
        public ActivityStatement(Statement base) {
            mBase = base;
        }
    
        @Override
        public void evaluate() throws Throwable {
            try {
                if (mLaunchActivity) {
                    mActivity = launchActivity(getActivityIntent());
                }
                mBase.evaluate();
            } finally {
                finishActivity();
                afterActivityFinished();
            }
        }
    }
    

    }

  4. 嗯,说实话,这是标准ActivityTestRule带有额外的getter getInstrumentation(),我将在我的测试类中使用。

    1. 创建一个新的Java类 - 它将是您的测试类并将代码放在下面:

      @RunWith(AndroidJUnit4.class) 
      public class MainActivityTest {
      
      private Solo solo;
      
      private static final String MAIN_ACTIVITY = MainActivity.class.getSimpleName();
      
      @Rule
      public MyActivityTestRule<MainActivity> mActivityRule = new MyActivityTestRule<>(MainActivity.class);
      
      @Before
      public void setUp() throws Exception {
          solo = new Solo(mActivityRule.getInstrumentation(), mActivityRule.getActivity());
      }
      
      @Test
      public void checkIfMainActivityIsProperlyDisplayed() throws InterruptedException {
          solo.waitForActivity("MainActivity", 2000);
          solo.assertCurrentActivity(mActivityRule.getActivity().getString(
                  R.string.error_no_class_def_found, MAIN_ACTIVITY), MAIN_ACTIVITY);
          solo.getText("Hello World").isShown();
      
        }
      }
      
    2. 正如你所看到的那样并不困难。

      希望它会有所帮助