Java 在AndroidAnnotations SharedReferences中模拟链式方法调用

Java 在AndroidAnnotations SharedReferences中模拟链式方法调用,java,android,unit-testing,mockito,android-annotations,Java,Android,Unit Testing,Mockito,Android Annotations,在我用于生成以下内容的项目中: 生成的类可以按如下方式使用: preferences.enabled().get(); preferences.enabled().put(true); public class MyLogicTest { @Mock SharedPreferences prefs; @Mock CustomSharedPreferences_ prefs_; MyLogic logic; @Before public

在我用于生成以下内容的项目中:

生成的类可以按如下方式使用:

preferences.enabled().get();
preferences.enabled().put(true);
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);
        }

    }
}
我试图编写一个单元测试来检查一些逻辑。在这里,我想模拟偏好:

@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.isValuesStoredProperty(MyLogicTest.java)上

可以用Mockito模拟链式方法调用(包括空对象)吗

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

preferences.enabled().get();
preferences.enabled().put(true);
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);
        }

    }
}
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
样本项目
解决方案
  • 请在上面的样本项目中找到工作

您是否尝试过使用
@RunWith(MockitoJUnitRunner.class)
而不是
MockitoAnnotations
运行测试?运行程序应该自动初始化所有
@Mock
带注释的字段,并删除该NPE


另外,下面的内容可能会对您有进一步的帮助。

为了模拟链式方法调用,您可以在
MyPreferences\uuu

@Mock(answer=Answers.RETURNS\u DEEP\u 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);
}
注意:您应该用
prefs.enabled()
返回的对象类型替换
MyObject
。使用这种方式,您可以更好地控制方法调用的行为

更新:如果
BooleanPrefField
是final,您只需在模拟中返回该类的对象即可

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

prefs.enabled()
返回什么?初始化
prefs
对象。这不是问题所在。当我在
prefs.enabled()
上调用
.get
时会发生NPE,它在实现案例中返回一个
BooleanPrefField
。我尝试过,但是
BooleanPrefField
final
。错误:
无法模拟/间谍类org.androidannotations.api.SharedReferences.BooleanPrefField Mockito无法模拟/间谍,因为:final class
BooleanPrefField
是包私有的,这会阻止我实例化新对象,例如
new BooleanPrefField(true)
。您可以尝试使用
SharedReferencesHelper.booleanField(“,false)
我看到这个类存在于
org.androidannotations.annotations.SharedReferences
包中。必须有一种方法来创建
booleanPrefield
对象。您可以传递模拟的
SharedReferences
对象。