使用Action 参数进行模拟方法

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

请考虑以下情形:

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

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

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

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

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

 public class SomeViewModel { private readonly IMeetingRoomService _meetingRoomService; public List 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); } } 

我想unit testingSomeViewModel.GetRooms()的实现。 (对于这个问题,我很快就编写了实现,但我实际上是在尝试使用TDD。)

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

 [Test] public void GetRooms_ShouldSetRooms() { var theRooms = new List { new MeetingRoom(1, "some room"), new MeetingRoom(2, "some other room"), }; var meetingRoomService = new Mock(); //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(); var shell = new ShellViewModel(meetingRoomService.Object); var theRooms = new List { new MeetingRoom(1, "some room"), new MeetingRoom(2, "some other room"), }; meetingRoomService .Setup(service => service.GetRooms(It.IsAny<Action<List>>())) .Callback((Action<List> action) => action(theRooms)); shell.GetRooms(); Assert.AreEqual(theRooms, shell.Rooms); } 

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

SetupCallback是你感兴趣的。

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

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

 Action> _getRoomsCallback = null; IMeetingRoomService _meetingRoomServiceFake; private void SetupCallback() { Mock.Get(_meetingRoomServiceFake) .Setup(f => f.GetRooms(It.IsAny>>())) .Callback((Action> cb) => _getRoomsCallback= cb); } [Setup] public void Setup() { _meetingRoomServiceFake = Mock.Of(); 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 { 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毫秒足以运行异步代码)。

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