2011-02-20 17 views
7

[單元測試新手] [C#]與行動<T>參數

懲戒方法考慮以下情形:

我使用Silverlight和調用WCF服務。 Silverlight只能異步調用WCF服務。我爲WCF服務構建一個包裝,以便我可以使用Action參數。 (使客戶端代碼更清潔)。

所以我必須檢索會議室異步服務。

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

將GetRooms變成List<MeetingRoom> GetRooms()不是一個選項。

我想使用這項服務的視圖模型設置一個公共屬性名爲客房。

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

編輯:

解決方案

閱讀斯蒂芬的回答第一。

這是測試代碼,我最後寫感謝斯蒂芬的回答是:

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

我會返回一個任務'' CodesInChaos

+0

ehhh .....什麼? –

回答

8

下面是一些僞代碼,我的天堂」運行它。但我認爲這就是你想要的。

SetupCallback是你所感興趣的東西。

對於所有的呼叫_meetingRoomServiceFake.GetRooms,只需將_getRoomsCallback爲傳入的參數。

現在你要,你是回調的引用傳入你的視圖模型,你可以用你想要測試的任何一個MeetingRooms列表來調用它。 因此,您可以像測試同步代碼一樣測試異步代碼。這只是一個更多的儀式來設置假。

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

我會試試這個,並給予反饋 –

+0

驚人的,它的工作原理!我完全不理解它,但它確實有效。我必須將_getRoomsCallBack設置爲null,否則代碼將無法編譯。而沒有實現的FYI這個測試在這一行中拋出了nullreference異常:「getRoomsCallback(theRooms);」因爲回調仍爲空 –

+0

我將添加詳細信息來解釋:) –

0

你可以使用的AutoResetEvent來處理異步調用。

只是初始化爲未設置和配置您的模擬服務將其設置在回調。 (IE: VAR mockService =新莫克(); mockService.SetUp(X => x.MyMethod())返回(someStuff).Callback(()=> handle.Set()); 。)

之後,我使用hadle.WaitOne(1000),以檢查它是否被調用。 (恕我直言,1000毫秒足以運行異步代碼)。

對不起:這本來是要作爲上述信息的迴應......我不能爲我的生活弄清楚如何回答:)