Метод насмешки с параметром Action <T> - PullRequest
9 голосов
/ 20 февраля 2011

[юнит-тестирование новичка] [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);
}

РЕДАКТИРОВАТЬ:

Решение

Сначала прочитайте ответ Стефана.

Этоэто тестовый код, который я написал в итоге благодаря ответу Стефана:

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

Ответы [ 2 ]

12 голосов
/ 20 февраля 2011

Вот какой-то псевдокод, я его не запускал. Но я думаю, что ты этого хочешь.

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);
}
0 голосов
/ 20 февраля 2011

Вы можете использовать AutoResetEvent для обработки асинхронных вызовов.

Просто инициализируйте его как неустановленный и настройте службу имитации, чтобы установить ее в обратном вызове.(IE: var mockService = new Mock (); mockService.SetUp (x => x.MyMethod ()). Возвращает (someStuff) .Callback (() => handle.Set ());)

После этого я использую hadle.WaitOne (1000), чтобы проверить, был ли он вызван.(ИМХО 1000 миллисекунд более чем достаточно для запуска асинхронного кода).

Извините: Это должно было пойти в ответ на пост выше ... Я не могу понять, какответить:)

...