java - 在 Android 代码中模拟创建 AsyncTasks 和 Intents 的优雅方法

标签 java android unit-testing mocking mockito

我有一个 Android Activity ,其中有类似的调用

final ConnectToServerAsyncTask task = new ConnectToServerAsyncTask(...);

final Intent intent = new Intent(this, SomeActivity.class);

为了对此类进行单元测试,我需要能够模拟 ConnectToServerAsyncTaskIntent 的创建(例如使用 Mockito)。

还有比下面描述的更优雅的方法吗?

public class MainActivityOfTheApp extends Activity {
    private IAsyncTaskFactory asyncTaskFactory = new AsyncTaskFactory();
    private IIntentFactory intentFactory = new IntentFactory();

    public void setAsyncTaskFactory(final IAsyncTaskFactory aFactory)
    {
        asyncTaskFactory = aFactory;
    }

    public void setIntentFactory(final IIntentFactory aFactory)
    {
        intentFactory = aFactory;
    }

    @Override
    protected void onResume() {
        ...
        final ConnectToServerAsyncTask task = asyncTaskFactory.create(...);
        ...

        final Intent intent = intentFactory.create(this, OtherActivity.class);
        ...
    }
}

在单元测试中,我将创建 MainActivityOfTheApp 的实例,然后使用 setAsyncTaskFactorysetIntentFactory 注入(inject)模拟。

最佳答案

PowerMock

您可以使用 PowerMock 模拟构造函数.

因此,在您的情况下,测试将如下所示:

@RunWith(PowerMockRunner.class)
@PrepareForTest(MainActivityOfTheApp.class)
public class MainActivityOfTheAppTest{
  private AsyncTaskFactory asyncTaskFactory;
  private IntentFactory intentFactory;
  private MainActivityOfTheApp mainActivityOfTheApp;

  @Before
  public void prepare() {
    asyncTaskFactory = PowerMockito.mock(AsyncTaskFactory.class);
    intentFactory = PowerMockito.mock(IntentFactory.class);
    PowerMockito.whenNew(AsyncTaskFactory.class).withNoArguments().thenReturn(asyncTaskFactory);
    PowerMockito.whenNew(IntentFactory.class).withNoArguments().thenReturn(intentFactory);
    mainActivityOfTheApp = new MainActivityOfTheApp();
  }

  @Test
  public void doTest() {
    //mainActivityOfTheApp has the mocks in final field inside, no need for the setters.
  }
}

我必须指出,PowerMock 功能强大,但经常在复杂的类加载(例如 OSGI 环境)或代码覆盖工具(例如 jacoco)方面出现问题。它通常可以工作,但我已经浪费了一些时间。

没有 PowerMock

第二种可能性是创建 package private (没有修饰符。为什么不是 protected 或 public?请参阅 What's wrong with overridable method calls in constructors? )方法,它调用构造函数如下:

public class MainActivityOfTheApp extends Activity {
    private final IAsyncTaskFactory asyncTaskFactory = constructAsyncTaskFactory();
    private final IIntentFactory intentFactory = constructIntentFactory();

    IAsyncTaskFactory constructAsyncTaskFactory()
    {
        return new AsyncTaskFactory();
    }

    IIntentFactory constructIntentFactory()
    {
        return new IntentFactory();
    }
    ...
}

然后在你的单元测试中(测试必须与被测试的类位于同一个包中!)你重写construct*()方法:

public class MainActivityOfTheAppTest{
  private AsyncTaskFactory asyncTaskFactory;
  private IntentFactory intentFactory;
  private MainActivityOfTheApp mainActivityOfTheApp;

  @Before
  public void prepare() {
    asyncTaskFactory = mock(AsyncTaskFactory.class);
    intentFactory = mock(IntentFactory.class);
    mainActivityOfTheApp = new HackedMainActivityOfTheApp();
  }

  @Test
  public void doTest() {
    //mainActivityOfTheApp has the mocks in final field inside, no need for the setters.
  }

  private class HackedMainActivityOfTheApp extends MainActivityOfTheApp {
        IAsyncTaskFactory constructAsyncTaskFactory()
        {
            return asyncTaskFactory;
        }

        IIntentFactory constructIntentFactory()
        {
            return intentFactory;
        }
  }
}

如果您对包含 MainActivityOfTheApp 的 jar 进行签名,则可以保护构造*方法。请参阅https://stackoverflow.com/a/968612/337621 :

Also, patches are harder (you have to re-sign the jar), class-patches are impossible (all classes in a single package must have the same signature source) and splitting jars becomes a chore.

关于java - 在 Android 代码中模拟创建 AsyncTasks 和 Intents 的优雅方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17389466/

相关文章:

java - Android - 动态更改在媒体播放器中播放的文件

java - Java中如何使用keystore来存储私钥?

c++ - 无法在 Visual Studio 2015 托管单元测试项目中打开 DLL 文件?

java - 用于客户端服务器模型的 REST 或 SOAP

android - 如何从 SQLite 中获取记录的总数

Angular 单元测试模拟 paramMap 获取

reactjs - 在 Jest 中测试 Axios

java - 具有 spring security 的应用程序之间是否共享 SecurityContextHolder

java - JAVA 中 XML 的有序列表级别

android - webview 加载包含 .js 的本地 html 文件作为 <script src>