【问题标题】:TDD and resources freeingTDD 和资源释放
【发布时间】:2016-11-20 19:19:10
【问题描述】:

我尝试使用 TDD 并得到以下代码:

public class ViewModel extends BaseObservable {

    private final Subscription subscription;

    public ReleasesViewModel(Observable<List<Data>> model) {

        subscription = model.subscribe(this::setData);       
    }

    public void destroy() { //method is not under test 
        subscription.unsubscribe();       
    }

    public List<Data> getData() {
        return data;
    }

    public void setData(List<Data> data) {
        this.data = data;
    }
}

我的测试:

public class ViewModelTest {

    @Test
    public void getData() {

        BehaviorSubject<List<Data>> observable = BehaviorSubject.create();
        ViewModel viewModel = new ViewModel(observable);

        List<Data> dataList = Arrays.asList(mock(Data.class), mock(Data.class));
        observable.onNext(dataList);

        assertTrue(viewModel.getData().equals(dataList));
    }
}

问题如下:

我应该验证subscription.unsubscribe(); 将被调用以释放资源,我可以将订阅包装在一些包装器中并通过构造函数注入依赖项,但我觉得我可以违反ViewModel 类的封装。经过大量的谷歌搜索,我没有找到任何关于在 TDD 实践中验证内存释放情况的线索。有人能给我指出这个案例的一些“最佳实践”吗?

【问题讨论】:

  • 为什么在构造函数中传入Subscription 会违反封装-它已经是您的ViewModel 类的成员?如果不是通过构造函数,它是如何被注入到你当前的代码中的?
  • 对不起,代码中的小错误。我从通过构造函数传递的“模型”获得“订阅”。
  • 为什么Subscription必须进入ViewModel内部?
  • Subscription 在 "subscribe" 方法中声明了从 observable 模型中获取数据的逻辑,并通过 setter 将数据放到视图模型中。逻辑不能在视图模型之外实现。
  • assertTrue(viewModel.getData().equals(dataList)); 你测试你的 viewModel 类的内部数据处理。 this 违反了封装性。

标签: java unit-testing tdd


【解决方案1】:

也许你有一些正确的 mvvm 测试示例

这就是我使用 Mockito 会做的事情(只是我的偏好,任何其他 Mocking 框架都可以......):

public class ViewModelTest {
    @Rule
    public MockitoRule rule = MockitoJUnit.rule();
    @Mock
    BehaviorSubject<List<Data>> observable;
    @Mock
    Subscription subscription;
    List<Data> data = Collections.emptyList();

    @Test
    public void Constructor_CalledWithObservable_subcribesSetDataMethod() {
        // prepare
        when(model.subscribe()).thenRetun(subscription);

        // act
        ViewModel viewModel = new ViewModel(observable);

        // assert
        verify(observable).subscribe(ViewModel::setData);
    }

    @Test
    public void destroy_unsubscribes() {
        // prepare
        when(model.subscribe()).thenRetun(subscription);    
        ViewModel viewModel = new ViewModel(observable);

        // act
        viewModel.destroy();    

        // assert
        verify(subscription).unsubscribe();
    }
}

【讨论】:

  • 如果您必须使用 .cache() 或 .delay() 或其他方法转换视图模型内部的观察者,您会怎么做?例如:ViewModel(Observable observable){ subscription = model.cache().subscribe(this::setData);
  • @DmitryBykov 不明白。该调用背后的业务相关行为是什么?我的意思是model 如何存储observable 是一个不应该测试的实现细节。你可能测试的是subscribe(this::setData)只被调用一次(验证方法已经做了什么......)
  • 我说的是这样的情况,当你不能在构造函数 public ViewModel(Observable&lt;String&gt; names){ subscription = names.filter(name -&gt; name.contain("Jon")).subscribe(); } 中模拟 observable 参数时
  • public ViewModel(List&lt;Observable&lt;String&gt;&gt; names){ subscription = names.filter(name -&gt; name.contain("Jon")).subscribe(); } 传入一个带有模拟 Observable 的真实 List,告诉 observable 返回有问题的名称并验证在模拟上调用了 subscribe() ...
  • 我无法验证在 mock 上调用了 'subscribe()',因为我不知道在构造函数中的可观察 'names' 上将调用哪个方法(如果有的话)。跨度>
猜你喜欢
  • 2014-08-12
  • 1970-01-01
  • 2012-01-08
  • 2012-05-04
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多