【问题标题】:Unit testing view model that uses SelectMany to call an async method in ReactiveUI使用 SelectMany 在 ReactiveUI 中调用异步方法的单元测试视图模型
【发布时间】:2018-10-23 13:36:48
【问题描述】:

我是 ReactiveUI 的新手,正在尝试测试如下所示的视图模型:

public interface IService
{
    Task<SessionModel> GetData(string id);
}

/// Provides a group of schedulers available to be used
public interface ISchedulers
{
    IScheduler Default { get; }
    IScheduler Dispatcher { get; }
}

public class MyVm : ReactiveObject
{
    IService service;

    public MyVm(ISchedulers schedulers, IService service)
    {
        this.service = service;
        this.session = this.WhenAnyValue(x => x.SessionId)
                           .SelectMany(SearchSession)
                           .ObserveOn(schedulers.Default)
                           .ToProperty(this, x => x.Session);
    }

    private async Task<SessionModel> SearchSession(string id)
    {
        return await this.service.GetData(id);
    }

    private string sessionId;
    public string SessionId
    {
        get => sessionId;
        set => this.RaiseAndSetIfChanged(ref sessionId, value);
    }


    readonly ObservableAsPropertyHelper<SessionModel> session;
    public SessionModel Session
    {
        get { return session.Value; }
    }

}

public class SessionModel { }

我正在模拟返回虚拟数据的服务调用,但不确定我需要对 TestScheduler 做什么才能让 SelectMany 工作。

这是一个测试类,展示了我将如何为视图模型创建测试。目标是最终能够检查模型是否已设置:

[TestClass]
public class MyVmTests
{
    [TestMethod]
    public void CreateClass
    {
       var subject = new MyVm(/*pass in mocks*/);

       subject.SessionId="test";

       Assert.IsNotNull(subject.Session);
     }
}

【问题讨论】:

  • ISchedulers 是什么?
  • 它提供了可能的调度器列表供可观察者使用。它在测试中被模拟返回TestScheduler。在实际代码中它返回System.Reactive.Concurrency.Scheduler.Default
  • 我更新了你的代码,我假设你有。您可以添加您要测试的内容吗?
  • 添加了测试类。
  • 我似乎无法在不添加更多单词的情况下添加更多代码。请注意,SessionId 将被正确定义为使用RaiseAndSetIfChanged

标签: unit-testing tdd system.reactive reactiveui


【解决方案1】:

我认为没有必要使用TestScheduler。以下是我的通行证(使用Moq):

var mockSchedulers = new Mock<ISchedulers>();
mockSchedulers.Setup(s => s.Default).Returns(Scheduler.Immediate);
var id = "123";
var mockService = new Mock<IService>();
var returnSession = new SessionModel();
mockService.Setup(s => s.GetData(It.Is<string>(i => i == id)))
    .ReturnsAsync(returnSession);
var target = new MyVm(mockSchedulers.Object, mockService.Object);

target.SessionId = id;

Assert.IsNotNull(target.Session);
Assert.AreEqual(returnSession, target.Session);

TestScheduler 最适合您尝试随时间测试某些东西(例如延迟,证明延迟确实发生了)。你在这里并没有真正做到这一点。

【讨论】:

  • 嗯,问题是我有一个实现ISchedulers 并使所有属性返回TestScheduler 的类。所以,如果我必须为每个测试使用不同的 IScheduler 模拟,以便我可以在 Default.ImmediateTestScheduler 之间切换,你建议我如何实现 vm.Session 以便在其他测试中我可以设置物有所值?
  • 没关系。通过使用TestScheduler 并在设置id 后调用Start() 使其工作。谢谢!!
猜你喜欢
  • 1970-01-01
  • 2017-12-21
  • 2014-01-18
  • 2020-02-19
  • 2013-03-14
  • 2020-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多