有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

AndroidAnnotations SharedReferences中的java模拟链式方法调用

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

import org.安卓annotations.annotations.sharedpreferences.DefaultBoolean;
import org.安卓annotations.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 at 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

样本项目

解决方案

  • 请在上面的样本项目中找到工作

共 (2) 个答案

  1. # 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);
    }
    

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

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

    when(prefs.enabled()).thenReturn(SharedPreferencesHelper.booleanField("", false));
    
  2. # 2 楼答案

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

    另外,也许跟随answer可以进一步帮助你