简体   繁体   English

如何使用Mockito模拟DisplayMetrics

[英]How to mock DisplayMetrics using Mockito

I have a custom view that I'm trying to test for different screen sizes, and I'm not able to find documentation or examples on how to correctly mock the display width in a unit test. 我有一个自定义视图,我正在尝试测试不同的屏幕尺寸,我无法找到有关如何在单元测试中正确模拟显示宽度的文档或示例。

Here's an example of how I'm trying to use this: 这是我试图使用它的一个例子:

private CustomTextView customTV;

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

    DisplayMetrics displayMetrics = mock(DisplayMetrics.class);
    displayMetrics.widthPixels = 600;

    Resources mockResources = mock(Resources.class);
    when(mockResources.getDisplayMetrics()).thenReturn(displayMetrics);

    Context mockContext = mock(Context.class);
    when(mockContext.getResources()).thenReturn(mockResources);

    customTV = new CustomTextView(mockContext);
}

@SmallTest
public void testViewSize() {
    Assert.assertEquals("Text size did not scale", 42, customTV.getTextSize());
}

Additionally, I'm getting an error on only some devices (the above implementation works for some screen sizes, but not all): 另外,我只在某些设备上出现错误(上述实现适用于某些屏幕尺寸,但不是全部):

Error in testViewSize:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.res.Configuration.isLayoutSizeAtLeast(int)' on a null object reference
    at android.view.ViewConfiguration.<init>(ViewConfiguration.java:284)
    at android.view.ViewConfiguration.get(ViewConfiguration.java:364)
    at android.view.View.<init>(View.java:3781)
    at android.view.View.<init>(View.java:3876)
    at android.widget.TextView.<init>(TextView.java:655)
    at android.widget.TextView.<init>(TextView.java:650)
    at android.widget.TextView.<init>(TextView.java:646)
    at com.foo.bar.view.CustomTextView.<init>(CustomTextView.java:55)
    at com.foo.bar.view.CustomTextView.<init>(CustomTextView.java:45)
    at com.foo.bar.view.CustomTextViewTests.setUp(CustomTextViewTests.java:52)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:557)
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1874)

All I really want to do is set the returned value of displayMetrics.widthPixels to a specific number, but because it's not a method call, like a getter or setter, I can't override it like I'm doing with the when and thenReturn methods in the other mock objects. 我真正想要做的就是将displayMetrics.widthPixels的返回值设置为一个特定的数字,但因为它不是方法调用,比如getter或setter,我不能像我正在使用whenthenReturn那样覆盖它其他模拟对象中的方法。

Is there an easier way to mock the Context/Resources/DisplayMetrics? 有没有更简单的方法来模拟Context / Resources / DisplayMetrics?

To answer your question, you mock it just like you are doing. 要回答你的问题,你就像你正在做的那样嘲笑它。

DisplayMetrics metrics = mock(DisplayMetrics.class);
metrics.widthPixels = 300;

Resources resources = mock(Resources.class);
when(resources.getDisplayMetrics()).thenReturn(metrics);

Assert.assertEquals(300, resources.getDisplayMetrics().widthPixels);
// ^^ see proof

The error message you posted simply suggests you need to mock out more objects. 您发布的错误消息只是建议您需要模拟更多对象。 You didn't do enough. 你做得不够。

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.res.Configuration java.lang.NullPointerException:尝试调用虚方法'boolean android.content.res.Configuration

So mock out the Configuration object as well. 所以也模拟了Configuration对象。

Edit: 编辑:

Code to show using a real configuration if you're using Robolectric: 如果您正在使用Robolectric,则使用实际配置显示的代码:

Context context = mock(Context.class);
Resources resources = mock(Resources.class);
when(resources.getConfiguration()).thenReturn(RuntimeEnvironment.application.getResources().getConfiguration());
when(context.getResources()).thenReturn(resources);

Ended up finding two different ways of doing this, both of which keep to at most only using Mockito as a testing library. 最后找到了两种不同的方法,这两种方法最多只能使用Mockito作为测试库。

Option 1: update the real Context 选项1:更新真实的Context

private CustomTextView customTV;

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

    Context context = getContext();
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();

    Configuration config = context.getResources().getConfiguration();
    displayMetrics.widthPixels = 600;
    // can optionally set the screen density as well:
    displayMetrics.densityDpi = DisplayMetrics.DENSITY_MEDIUM;
    config.densityDpi = DisplayMetrics.DENSITY_MEDIUM;

    context.getResources().updateConfiguration(config, displayMetrics);

    customTV = new CustomTextView(mockContext);
}

@SmallTest
public void testViewSize() {
    Assert.assertEquals("Text size did not scale", 42, customTV.getTextSize());
}

Option 2: Use ContextWrapper + Mockito.spy 选项2:使用ContextWrapper + Mockito.spy

private CustomTextView customTV;

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

    Context context = new ScreenWidthContextWrapper(getContext(), 600);

    customTV = new CustomTextView(context);
}

private class ScreenWidthContextWrapper extends ContextWrapper {
    private final DisplayMetrics mDisplayMetrics;
    private final Resources mSpyResources;

    public ScreenWidthContextWrapper(Context realContext, int widthPixels) {
        super(realContext);

        Resources realResources = realContext.getResources();

        mDisplayMetrics = new DisplayMetrics();
        mDisplayMetrics.widthPixels = widthPixels;
        mDisplayMetrics.setTo(realResources.getDisplayMetrics());

        mSpyResources = spy(realResources);
        when(mSpyResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
    }

    @Override
    public Resources getResources() {
        return mSpyResources;
    }
}

@SmallTest
public void testViewSize() {
    Assert.assertEquals("Text size did not scale", 42, customTV.getTextSize());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM