51

I'm setting up some unit tests and using Rhino Mocks to populate the object being tested. One of the things being mocked is a Task<HttpResponseMessage>, since the logic being tested includes a call to an HttpClient to get an async response.

So I've started setting up the mocks like this:

var httpClient = MockRepository.GenerateMock<HttpClient>();
var taskFunc = MockRepository.GenerateMock<Func<HttpResponseMessage>>();
var responseTask = MockRepository.GenerateMock<Task<HttpResponseMessage>>(taskFunc);
var response = MockRepository.GenerateMock<HttpResponseMessage>();

httpClient.Stub(c => c.PostAsJsonAsync<IEnumerable<LogMessage>>(Arg<string>.Is.Anything, Arg<IEnumerable<LogMessage>>.Is.Anything)).Return(responseTask);
responseTask.Stub(t => t.Result).Return(response);
response.Stub(r => r.IsSuccessStatusCode).Return(true);

(The "act" step of the test will be to instantiate the object being tested, feed it the httpClient, and run a method on it. The "assert" will verify via the mocks that expected method calls were made on them.)

Stepping through this in a debugger, there's an indefinite hang on this line:

responseTask.Stub(t => t.Result).Return(response);

I don't have a lot of experience with Rhino Mocks or with C# async, so I may be overlooking something obvious. The goal, of course, is that any call to the .Result property would return the response mock. But it looks like my attempt itself is perhaps invoking .Result which I would expect to wait indefinitely since it's just a mock, perhaps?

What is the right way to arrange this? Essentially I need to supply my object with a mocked HttpClient and assert that a method was called on it with a specific argument.

David
  • 208,112
  • 36
  • 198
  • 279
  • 1
    I did a lot of mocking around Tasks about a year or so ago using MOQ. Unless I am wrong, you just need to replace the taskFunc you are passing into your mock responseTask. Then have that mock function immediately return a response. – Keith May 01 '14 at 15:28
  • @Keith: Interesting. So just create a real `Func` instead of a mock, which only returns? I just tried as such: `Func taskFunc = delegate() { return response; };` and passed that `taskFunc` to the `responseTask` mock, but the same behavior is observed. – David May 01 '14 at 15:43
  • 1
    I don't think you need an actual Mock Task. What you need to Mock is the function that is passed into the Task, so it will just return "x". – Keith May 01 '14 at 15:45

1 Answers1

83

The simplest thing is just to return a completed task with the expected result:

var responseTask = Task.FromResult(response);

I imagine reason this hangs is that the mocked task is never started and hence the given func is not run. You could start it in your test:

var responseTask = MockRepository.GenerateMock<Task<HttpResponseMessage>>(taskFunc);
responseTask.Start();

However there's no reason to mock tasks since you can easily create completed/failed/cancelled tasks directly.

Lee
  • 142,018
  • 20
  • 234
  • 287
  • Looks like that's doing the trick. With this I also needed to remove by latter two `Stub()` calls. (`.Result` is handled by your answer, `.IsSuccessStatusCode` was throwing an error when attempting to stub it but seems to default to `true` in the mock anyway.) Thanks! – David May 01 '14 at 15:50