繁体   English   中英

使用Action <T>参数进行模拟方法

[英]Mocking method with Action<T> parameter

[单位测试新手] [c#]

请考虑以下情形:

我正在使用Silverlight并调用WCF服务。 Silverlight只能异步调用WCF服务。 我构建了一个围绕WCF服务的包装器,以便我可以使用Action参数。 (使客户端代码更清洁)。

所以我有一个异步服务来检索会议室。

public interface IMeetingRoomService
{
    void GetRooms(Action<List<MeetingRoom>> result);
}

将GetRooms转换为List<MeetingRoom> GetRooms()不是一个选项。

我想在ViewModel中使用此服务来设置名为Rooms的公共属性。

public class SomeViewModel
{
    private readonly IMeetingRoomService _meetingRoomService;

    public List<MeetingRoom> Rooms { get; set; }

    public SomeViewModel(IMeetingRoomService meetingRoomService)
    {
        this._meetingRoomService = meetingRoomService;
    }

    public void GetRooms()
    {
        // Code that calls the service and sets this.Rooms
        _meetingRoomService.GetRooms(result => Rooms = result);
    }
}

我想单元测试SomeViewModel.GetRooms()的实现。 (对于这个问题,我很快写了实现,但实际上我正在尝试使用TDD。)

我该如何完成此测试? 我正在使用NUnit和Moq。

[Test]
public void GetRooms_ShouldSetRooms()
{
    var theRooms = new List<MeetingRoom>
                       {
                           new MeetingRoom(1, "some room"),
                           new MeetingRoom(2, "some other room"),
                       };

    var meetingRoomService = new Mock<IMeetingRoomService>();

    //How do I setup meetingRoomService so that it gives theRooms in the Action??


    var viewModel = new SomeViewModel(meetingRoomService.Object);

    viewModel.GetRooms();

    Assert.AreEqual(theRooms, viewModel .Rooms);
}

编辑:

首先阅读Stephane的回答。

这是测试代码我最后写的感谢stephane的答案:

[Test]
public void GetRooms_ShouldSetRooms()
{
    var meetingRoomService = new Mock<IMeetingRoomService>();
    var shell = new ShellViewModel(meetingRoomService.Object);
    var theRooms = new List<MeetingRoom>
                       {
                           new MeetingRoom(1, "some room"),
                           new MeetingRoom(2, "some other room"),
                       };

    meetingRoomService
        .Setup(service => service.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
        .Callback((Action<List<MeetingRoom>> action) => action(theRooms));

    shell.GetRooms();

    Assert.AreEqual(theRooms, shell.Rooms);
}

这是一些伪代码,我还没有运行它。 但我认为这就是你想要的。

SetupCallback是你感兴趣的。

对于_meetingRoomServiceFake.GetRooms的所有调用,只需将_getRoomsCallback设置为传入的参数即可。

您现在可以引用要在viewmodel中传递的回调,并且可以使用要测试它的MeetingRoom列表来调用它。 因此,您可以像验证同步代码一样测试异步代码。 设置假货只是一个更多的仪式。

Action<List<MeetingRoom>> _getRoomsCallback = null;
IMeetingRoomService _meetingRoomServiceFake;


private void SetupCallback()
{
     Mock.Get(_meetingRoomServiceFake)
         .Setup(f => f.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
         .Callback((Action<List<MeetingRoom>> cb) => _getRoomsCallback= cb);
}

[Setup]
public void Setup()
{
     _meetingRoomServiceFake = Mock.Of<IMeetingRoomService>();
     SetupCallback();
}

[Test]
public void Test()
{

      var viewModel = new SomeViewModel(_meetingRoomServiceFake)

      //in there the mock gets called and sets the _getRoomsCallback field.
      viewModel.GetRooms();
      var theRooms = new List<MeetingRoom>
                   {
                       new MeetingRoom(1, "some room"),
                       new MeetingRoom(2, "some other room"),
                   };

     //this will call whatever was passed as callback in your viewModel.
     _getRoomsCallback(theRooms);
}

您可以使用AutoResetEvent来处理异步调用。

只需将其初始化为unset并配置您的模拟服务以在回调中设置它。 (IE:var mockService = new Mock(); mockService.SetUp(x => x.MyMethod())。返回(someStuff).Callback(()=> handle.Set());)

之后我使用hadle.WaitOne(1000)来检查它是否被调用。 (恕我直言1000毫秒足以运行异步代码)。

对不起:这应该是作为对上面帖子的回复...我不能为我的生活弄清楚如何回复:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM