【问题标题】:Mocking chained method calls in AndroidAnnotations SharedPreferences在 AndroidAnnotations SharedPreferences 中模拟链式方法调用
【发布时间】:2017-05-18 14:24:34
【问题描述】:

在一个项目中,我使用AndroidAnnotations 生成SharedPreferences

import org.androidannotations.annotations.sharedpreferences.DefaultBoolean;
import org.androidannotations.annotations.sharedpreferences.SharedPref;

@SharedPref(value = SharedPref.Scope.UNIQUE)
public interface MySharedPreferences {

    @DefaultBoolean(false)
    boolean enabled();
}

生成的类可以如下使用:

preferences.enabled().get();
preferences.enabled().put(true);

我正在尝试编写一个检查一些逻辑的单元测试。在那里我想模拟偏好:

@Mock MyPreferences_ prefs;
MyLogic logic;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    logic = new Logic();
}

@Test
public void testEnabled() throws Exception {
    when(prefs.enabled().get()).thenReturn(false);
    assertThat(logic.isEnabled()).isEqualTo(false);
}

但是,访问prefs.enabled() 会抛出NullPointerException

java.lang.NullPointerException 在 com.example.MyLogicTest.isValuesStoredProperly(MyLogicTest.java) ...

是否可以使用 Mockito 模拟链式方法调用(包括空对象)?

更新

作为基于helpful suggestions by alayor 的更新,我将实现更改如下:

public class MyLogicTest {

    @Mock SharedPreferences        prefs;
    @Mock CustomSharedPreferences_ prefs_;

    MyLogic logic;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        logic = new MyLogic();
    }

    @Test
    public void testEnabled() throws Exception {
        MockPrefs mockPrefs = new MockPrefs(prefs);
        when(prefs_.enabled()).thenReturn(mockPrefs.getEnabledPrefField());
        doNothing().when(prefs_.enabled()).put(anyBoolean()); // <-- fails
        when(prefs_.enabled().get()).thenReturn(false);
        assertThat(logic.isEnabled()).isEqualTo(false);
    }

    private class MockPrefs extends SharedPreferencesHelper {

        public MockPrefs(SharedPreferences sharedPreferences) {
            super(sharedPreferences);
        }

        public BooleanPrefField getEnabledPrefField() {
            return super.booleanField("enabled", enabledOld);
        }

    }
}

这里仍然失败

doNothing().when(prefs_.enabled()).put(anyBoolean());

来自prefs_.enabled()BooleanPrefField 对象是final,不能被模拟。

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at MyLogicTest.testEnabled

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, which is not supported
 3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

示例项目

解决方案

  • 请在上面的示例项目中找到工作。

【问题讨论】:

  • prefs.enabled() 返回什么?

标签: java android unit-testing mockito android-annotations


【解决方案1】:

为了模拟链式方法调用,您可以在 MyPreferences_ 中使用此注解

@Mock(answer = Answers.RETURNS_DEEP_STUBS).

但我建议您改为模拟调用 prefs.enabled() 的结果。

@Mock 
BooleanPrefField booleanPrefField;

@Test
public void testEnabled() throws Exception {
    when(prefs.enabled()).thenReturn(booleanPrefField);
    when(booleanPrefField.get()).thenReturn(false);
    assertThat(logic.isEnabled()).isEqualTo(false);
}

注意:您应该将MyObject 替换为prefs.enabled() 返回的对象类型。使用这种方式,您可以更好地控制方法调用的行为。

更新:如果 BooleanPrefField 是最终的,您可以在模拟中简单地返回该类的对象。

when(prefs.enabled()).thenReturn(SharedPreferencesHelper.booleanField("", false));

【讨论】:

  • 我试过了,但BooleanPrefFieldfinal。错误:Cannot mock/spy class org.androidannotations.api.sharedpreferences.BooleanPrefField Mockito cannot mock/spy because: final class
  • BooleanPrefField 是包私有的,它阻止我实例化一个新对象,例如new BooleanPrefField(true)
  • 你可以尝试使用SharedPreferencesHelper.booleanField("", false)
  • 我看到这个类存在于org.androidannotations.annotations.sharedpreferences 包中。必须有一种方法可以创建BooleanPrefField 对象。 javadox.com/org.androidannotations/androidannotations-api/3.2/…
  • 你可以传递一个模拟的 SharedPreferences 对象。
【解决方案2】:

您是否尝试过使用 @RunWith(MockitoJUnitRunner.class) 而不是 MockitoAnnotations 运行测试?跑步者应该自动初始化所​​有 @Mock 注释字段并摆脱该 NPE。

也许关注answer 可以进一步帮助您。

【讨论】:

  • prefs 对象已初始化。那不是问题。当我在 prefs.enabled() 上调用 .get 时发生 NPE,在实现情况下返回 BooleanPrefField
猜你喜欢
  • 2017-07-05
  • 1970-01-01
  • 2014-07-08
  • 2011-07-16
  • 1970-01-01
  • 1970-01-01
  • 2017-10-14
  • 1970-01-01
  • 2018-10-09
相关资源
最近更新 更多