使用Action< T>的模拟方法参数 [英] Mocking method with Action<T> parameter

查看:94
本文介绍了使用Action< T>的模拟方法参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

[unit testing newbie] [c#]

请考虑以下情形:

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

I'm using Silverlight and calling a WCF service. Silverlight can only call WCF services asynchronously. I build a wrapper around the WCF service so that I can work with Action parameters. (makes the client code a lot cleaner).

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

So I have an async service that retrieves meeting rooms.

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

将GetRooms转换为 List< MeetingRoom>不能选择GetRooms()

Turning GetRooms into List<MeetingRoom> GetRooms() is not an option.

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

I want to use this service in a ViewModel to set a public property called 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。)

I want to unit test the implementation of SomeViewModel.GetRooms(). (For this question I quickly wrote the implementation but I'm actually trying to use TDD.)

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

How do I finish this test? I'm using NUnit and 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的答案。

Read Stephane's answer first.

这是我的测试代码最终由于斯蒂芬的回答而写作:

This is the Test code I ended up writing thanks to stephane's answer:

[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);
}


推荐答案

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

Here is some pseudo code, I haven't run it. But I think that's what you want.

SetupCallback是您感兴趣的。

SetupCallback is what you are interested in.

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

For all the calls to _meetingRoomServiceFake.GetRooms, simply set the _getRoomsCallback to the parameter passed in.

现在,您已经引用了在视图模型中传递的回调,您可以调用它与您要测试的MeetingRoom的任何列表一起。
因此,您几乎可以像测试同步代码一样测试异步代码。

You now have a reference to the callback that you are passing in your viewmodel, and you can call it with whatever list of MeetingRooms you want to test it. So you can test your asynchronous code almost the same way as synchronous code. it's just a bit more ceremony to setup the fake.

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);
}

这篇关于使用Action&lt; T&gt;的模拟方法参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆