Android JUnit测试在一起运行时挂起

时间:2013-01-31 03:13:33

标签: java android junit cryptography

我会提前为发布大量代码而道歉,这个问题确实让我感到高兴!

我有两个Android JUnit测试导致了我的问题。单独运行它们并且它们工作正常,但是当一起运行时(PasswordEntryActivityTests然后CryptoKeystoreTests),CryptoKeystoreTests会无限期地挂起。

我知道这不仅仅是模拟器很慢,因为每个单独完成的时间不到一秒,但它可以挂起超过20分钟。我还在真实设备(Droid Razr)上进行了测试,它也做了同样的事情。

有问题的代码是PasswordEntryActivity.launchNewPasswordActivity()。删除该功能可以使一切正常。

在调试器挂起时暂停调试器中的函数说明它在:

MessageQueue.nativePollOnce(int, int) line: not available [native method]

发生了什么事?

我在下面复制了:

  • PasswordEntryActivity
  • PasswordEntryActivityTests
  • CryptoManagerKeystoreTests

请让我知道发布您想要查看的任何其他代码。 谢谢!

    public class PasswordEntryActivity extends Activity 
    {
...
        private void launchNewPasswordActivity()
        {
            Intent launchNewPasswordIntent = new Intent(this, NewPasswordActivity.class);
            startActivity(launchNewPasswordIntent);
        }

        @Override
        protected void onCreate(Bundle savedInstanceState) 
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.password_entry_layout);
...
            //this code should be LAST in onCreate because it exits the Activity
            //CryptoManager.passwordIsRight returns 0 if no password has been set
            passwordExists = CryptoManager.passwordIsRight("x", this) != 0;
            if(!passwordExists)
                launchNewPasswordActivity();
        }
    }

该活动的测试:

    //supposed to make sure the application responds correctly when no password is set
    public class PasswordEntryActivityTests extends android.test.ActivityInstrumentationTestCase2<                          crypnote.controller.main.PasswordEntryActivity>{
    protected void setUp() throws Exception 
    {
        passwordEntryActivity = getActivity();

        //delete the database if it exists
        File file = passwordEntryActivity.getFileStreamPath(DBInterface.Constants.DatabaseName);
        if(file.exists())
            assertTrue(file.delete());
        file = passwordEntryActivity.getFileStreamPath(CryptoManager.Constants.KEYSTORE_PATH);
        if(file.exists())
            assertTrue(file.delete());
    }
    //allows us to access the interface
    @UiThreadTest
    public void testNoPassword() throws Exception
    {
        passwordEntryActivity  = getActivity();

        EditText passwordEntryEditText = 
                (EditText) passwordEntryActivity.findViewById(
                crypnote.controller.main.R.id.passwordentrylayout_passwordedittext);
        Button unlockButton = (Button) passwordEntryActivity.findViewById(
                                        crypnote.controller.main.R.id.passwordentrylayout_unlockbutton);


        int passwordResult = CryptoManager.passwordIsRight("x", getActivity());
        assertTrue(passwordResult == 0);

        //pass a wrong password to the edittext and click the unlock button
        passwordEntryEditText.setText("x");
        assertTrue(unlockButton.performClick());

        //get the foreground activity class name
        ActivityManager am = (ActivityManager) passwordEntryActivity.
                                                        getSystemService(Context.ACTIVITY_SERVICE);

        // get the info from the currently running task
        List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1); 

        ComponentName componentInfo = taskInfo.get(0).topActivity;
        String foregroundClassName = componentInfo.getShortClassName();

        //don't forget the leading '.'
        assertTrue(!foregroundClassName.equals(".PasswordEntryActivity"));
    }
}

CryptoKeystoreTests:

public class CryptoKeystoreTests extends android.test.ActivityInstrumentationTestCase2<
                                                                crypnote.controller.main.PasswordEntryActivity>
{
    public void testKeystore() throws Exception
    {
        Context context = getActivity();

        //delete the database if it exists
        File file = context.getFileStreamPath(DBInterface.Constants.DatabaseName);
        if(file.exists())
            assertTrue(file.delete());
        file = context.getFileStreamPath(CryptoManager.Constants.KEYSTORE_PATH);
        if(file.exists())
            assertTrue(file.delete());

        CryptoManager cryptoManager=null;
        String password = CryptoManager.Constants.DEBUG_PASSWORD;
        FileInputStream fis=null;

            //the cryptomanager will generate a new key and keystore
            cryptoManager = new CryptoManager(password, context);
            Key CRYPTOKEY = cryptoManager.getKey();
            cryptoManager.close();

            //initialize KeyStore
            KeyStore keystore = KeyStore.getInstance(Constants.KEYSTORE_INSTANCE_TYPE);
            fis = context.openFileInput(CryptoManager.Constants.KEYSTORE_PATH);
            keystore.load(fis, password.toCharArray());

            assertTrue(keystore.containsAlias(Constants.APP_ALIAS));    
            assertTrue(keystore.isKeyEntry(Constants.APP_ALIAS));

            Key key = keystore.getKey(CryptoManager.Constants.APP_ALIAS, 
                                        password.toCharArray());
            assertTrue(key.getAlgorithm().equals(CryptoManager.Constants.PROVIDER_NAME));

            assertTrue(key.getAlgorithm().equals(CRYPTOKEY.getAlgorithm()));
            assertTrue(key.getFormat().equals(CRYPTOKEY.getFormat()));

            if(fis != null)
                fis.close();
    }
}

编辑:NewPasswordActivity.onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.new_password_layout);
}

1 个答案:

答案 0 :(得分:2)

它会挂起,因为PasswordEntryActivityTests不会释放/完成自己在自己的运行生命周期中已经解决/创建的资源/ UI事件,更具体地说,然后是新打开的NewPasswordActivity。

PasswordEntryActivityTests首先测试PasswordEntryActivity的创建,即getActivity(),因此,根据条件,启动第二个NewPasswordActivity,新打开的NewPasswordActivity占用前台窗口并永久保留,开发人员有责任完成测试后正确释放它。

在检测测试中,检测/监控当前活动的第二个活动启动的正确方法是使用ActivityMonitor,请参阅下面的伪代码:

// No password result starting a second activity.
public void testNoPassword() {
  // register NewPasswordActivity that need to be monitored.
  ActivityMonitor activityMonitor = getInstrumentation().addMonitor(NewPasswordActivity.class.getName(), null, false);

  // Get current activity, it will start NewPasswordActivity in consequence.
  PasswordEntryActivity currentActivity = getActivity();

  NewPasswordActivity nextActivity = getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 5);
  // NewPasswordActivity is opened and captured.
  assertNotNull(nextActivity);
  // Don't forget to release/finish NewPasswordActivity after test finish.
  nextActivity.finish();
}