使用mockito来模拟AccountManager

时间:2014-11-14 18:57:01

标签: android mockito

我正在使用mockito在Activity测试中模拟AccountManager。

所以,我的测试代码如下:

public class PressuresListActivityUnitTest extends
    ActivityUnitTestCase<PressuresListActivity> {

// Test data.
private static final String ACCOUNT_TYPE = "com.example.android";
private static final Account ACCOUNT_1 = new Account("account1@gmail.com", ACCOUNT_TYPE);
private static final Account ACCOUNT_2 = new Account("account2@gmail.com", ACCOUNT_TYPE);
private static final Account[] TWO_ACCOUNTS = { ACCOUNT_1, ACCOUNT_2 };

@Mock
private AccountManager mMockAccountManager;

public PressuresListActivityUnitTest() {
    super(PressuresListActivity.class);
}

@Override
protected void setUp() throws Exception {
    super.setUp();

    setupDexmaker();
    // Initialize mockito.
    MockitoAnnotations.initMocks(this);
}

public void testAccountNotFound() {
    Mockito.when(mMockAccountManager.getAccounts())
            .thenReturn(TWO_ACCOUNTS);

    Intent intent = new Intent(Intent.ACTION_MAIN);
    startActivity(intent, null, null);
}

/**
 * Workaround for Mockito and JB-MR2 incompatibility to avoid
 * java.lang.IllegalArgumentException: dexcache == null
 *
 * @see <a href="https://code.google.com/p/dexmaker/issues/detail?id=2">
 *     https://code.google.com/p/dexmaker/issues/detail?id=2</a>
 */
private void setupDexmaker() {
    // Explicitly set the Dexmaker cache, so tests that use mockito work
    final String dexCache = getInstrumentation().getTargetContext().getCacheDir().getPath();
    System.setProperty("dexmaker.dexcache", dexCache);
}

将要测试的onCreate活动方法:

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

    AccountManager am = AccountManager.get(this);
    Account[] accounts = am.getAccounts();
    if (accounts.length > 0) {
        Log.i("TAG", "it works!");
    }
}

但是当我运行测试时,AccountManager.getAccounts不会返回测试中指定的帐户。

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

这不是mockito的工作方式。

import static org.mockito.Mockito.*;
...

public void testAccountNotFound() {

    AccountManager am = mock(AccountManager.class);
    when(am.getAccounts()).thenReturn(TWO_ACCOUNTS);

    // this is how you unit test something
    assertTrue(am.getAccounts().size == 2);
}

public void testMoreRealWorldExample() {

    AccountManager am = mock(AccountManager.class);
    when(am.getAccounts()).thenReturn(TWO_ACCOUNTS);

    /* try and create an account; createNewAccount() will call
       getAccounts() to find out how many accounts there already
       are in the system, and due to the above injection, it would
       think there are already two. Thus we can test to make sure
       users cannot create three or more accounts.
     */
    boolean accountCreated = am.createNewAccount();

    // maximum two accounts are allowed, so this should return false.
    assertFalse(accountCreated);
}

您无法直接使用mockito只是在对象中随意注入值,然后运行Activity。 Mockito用于对您的对象进行单元测试,理想情况是对Android特定对象的引用最少,但有些引用是不可避免的。

请阅读the cookbook more closely,因为它非常详尽。

如果你想模拟和整个Activity,你需要查看in to Robolectric

答案 1 :(得分:2)

经过一番研究,我终于解决了这个问题。

Android提供了一些在测试中使用的类,如MockContext,IsolatedContext。

http://developer.android.com/reference/android/test/mock/MockContext.html

http://developer.android.com/reference/android/test/IsolatedContext.html

为了完成这项工作,我创建了ContextWrapper和overrode(??)getSystemService方法的子类。

根据文件:

&#34;代理Context的实现,简单地将其所有调用委托给另一个Context。可以进行子类化以修改行为而不更改原始上下文。&#34;

http://developer.android.com/reference/android/content/ContextWrapper.html

这样,我使用常规的AndroidActivityUnitTestCase注入原始上下文,但修改后符合我的需要,在Activity中。

检查出来:

public class FakeContextWrapper extends ContextWrapper {

    private static final String ACCOUNT_TYPE = "com.example.android";

    private static final Account ACCOUNT_1 = new Account("account1@gmail.com", ACCOUNT_TYPE);
    private static final Account ACCOUNT_2 = new Account("account2@gmail.com", ACCOUNT_TYPE);

    private static final Account[] TWO_ACCOUNTS = { ACCOUNT_1, ACCOUNT_2 };

    @Mock
    private AccountManager mMockAccountManager;

    public FakeContextWrapper(Context base) {
        super(base);

        MockitoAnnotations.initMocks(this);
        Mockito.when(mMockAccountManager.getAccounts()).thenReturn(TWO_ACCOUNTS);
    }

   @Override
   public Object getSystemService(String name) {
       if (Context.ACCOUNT_SERVICE.equals(name)) {
           return mMockAccountManager;
       } else {
           return super.getSystemService(name);
       }
   }
}

测试内部:

public void testAccountNotFound() {
    Context context = new FakeContextWrapper(getInstrumentation().getTargetContext());
    setActivityContext(context);
    Intent intent = new Intent(Intent.ACTION_MAIN);
    startActivity(intent, null, null);
    // TODO assertions.
}

最后,被测活动:

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

    AccountManager am = AccountManager.get(this);
    Account[] accounts = am.getAccounts();
    if (accounts.length == 0) {
        // TODO call login.
    } else {
        Log.i("TAG", "it works!");
    }
}