【问题标题】:Field value not set when using mock and abstract class使用模拟和抽象类时未设置字段值
【发布时间】:2016-07-12 22:27:22
【问题描述】:

由于某些无法解释的原因,我在构造函数中分配的字段 (currentExp) 在使用模拟进行单元测试时未正确设置。我正在分配字段currentExp,方法是使用我的Storage 类(使用SharedPreferences)通过loadExperience 方法加载它。当我对此进行单元测试时,我想模拟 Storage 类,所以 loadexperience 返回值 10。

这是我的具体Experience 类:

public class Experience extends StorageObject {

    private int currentExp = 0;

    public Experience() {
        this(new Storage());
    }

    @VisibleForTesting
    protected Experience(Storage storage) {
        super(storage);
    } // Debug point #2

    @Override
    protected void init(Storage storage) {
        this.currentExp = storage.loadExperience();
    } // Debug point #1
}

它扩展了StorageObject:

public abstract class StorageObject {
    protected Storage storage;

    protected StorageObject() {
        this(new Storage());
    }

    @VisibleForTesting
    protected StorageObject(Storage storage) {
        this.storage = storage;
        init(storage);
    }

    protected abstract void init(Storage storage);
}

这是我的单元测试:

@Test
public void testConstructor_StorageValuePositive_IsSetAsCurrentExp() {
    int expectedSavedExp = 10;
    Storage storageMock = mock(Storage.class);
    doReturn(expectedSavedExp).when(storageMock).loadExperience();

    Experience exp = new Experience(storageMock);

    assertEquals(expectedSavedExp, exp.getCurrentExp());
}

在调试时,我发现模拟确实有效,并且在调试点 #1 将值 10 分配给 currentExp。然后不久之后,在调试点 #2 处,该值似乎又为 0。

任何人都知道这里发生了什么,以及如何解决这个问题?

【问题讨论】:

    标签: android unit-testing constructor mocking abstract-class


    【解决方案1】:

    这里的问题是初始化顺序。超级构造函数首先发生,然后是字段初始化。

    因此,您的构造函数将其超级调用中的 currentExp 设置为 10,然后该字段将初始化为 0。

    那你能做什么?一些想法: 将currentExp 移动到父类或者不给它一个默认值。

    更多阅读材料:

    http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5

    https://stackoverflow.com/a/14806340/5842844

    【讨论】:

    • 太好了,谢谢!我猜你的意思是currentExp 而不是expectedSavedExp? ;-) 但我知道如何解决它,这才是最重要的。 ...另一个想法:因为我已经将storage 设置为StorageObject 中的一个字段,所以我还可以使用storage.loadExperience() 作为默认值来初始化currentExp,而不是在构造函数中分配它。这样我也可以省略abstract init 方法。这似乎解决了单元测试问题,但这样做是否常见/正确?
    • ups 是的,我的意思是 currentExp。您可以将 init 移动到子类中,没有理由将其放在父类中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 2016-12-05
    • 1970-01-01
    • 2011-12-31
    • 1970-01-01
    相关资源
    最近更新 更多