biofractal
biofractal

Reputation: 19133

NancyFX: How do I deserialize dynamic types via BrowserResponse.Body.DeserializeJson (unit tests)

I have the following NancyFX unit test. I use the Shouldly assertion library to give the set of extensions methods that start .Should---

[Fact]
public void Assessment__Should_return_assessment_state_for_specified_user()
{
    const AssessmentState assessmentState = AssessmentState.Passed;
    var user = Fake.Mentor();

    using (var db = Fake.Db())
    {
        db.Save(user);
        Fake.Assessment(user.Id, db, assessmentState);
        db.ClearStaleIndexes();
    }

    var response = Fake.Browser(user.UserName, user.Password)
            .Get("/assessment/state/" + user.Id, with => with.HttpRequest());

    //var result = (dynamic)body.DeserializeJson<ExpandoObject>();
    var result = (dynamic) JsonConvert.DeserializeObject<ExpandoObject>(response.Body.AsString());

    result.ShouldNotBe(null);
    ((AssessmentState) result.State).ShouldBe(assessmentState);
}

This test calls a AssessmentService uri defined as /assessment/state/" + user.Id which returns a simple JSON object definition that has a single property State of type (enum) AssessmentState, either Passed, Failed or NotStarted.

Here is the service handler so you can see there are no tricks.

Get["/assessment/state/{userid}"] = parameters =>
    {
        var assessment = AssessmentService.GetByUserId(Db, (string)parameters.userid);
        return assessment == null ? HttpStatusCode.NotFound : Response.AsJson(new
                                                                        {
                                                                            assessment.State
                                                                        });
    };

And here is an example the JSON this service call returns:

{"State":1}

Everything works fine until I try to Deserialize the JSON returned by the fake Nancy browser. First I tried to use the built in method provided by Nancy's BrowserResponse.Body object:

var result = (dynamic)response.Body.DeserializeJson<ExpandoObject>();

This deserializes to an empty object. Which is no good. However, if we use the Newtonsoft equivalent then everything is fine (almost).

var result = (dynamic) JsonConvert.DeserializeObject<ExpandoObject>(response.Body.AsString());

The JSON deserialization now works and so the following Shouldly assertion passes with flying colours:

((AssessmentState) result.State).ShouldBe(assessmentState);

However, for reasons that I suspect have to do with anonymous types, the following line fails at run-time (it compiles fine).

result.ShouldNotBe(null);

Shouldly Exception

That is quite a lot of information. Let me distil it down to two questions:

  1. Why does Nancy's built in JSON deserializer not work given that the Newtonsoft version does?

  2. How do I work with the dynamic types generated by the JSON de-serialisation so that the Shouldly extension methods do not cause a run-time exception?

Thanks

Upvotes: 2

Views: 1928

Answers (1)

Xerxes
Xerxes

Reputation: 309

I can't answer the first question, but WRT Shouldly and dynamic types, Shouldly's ShouldNotBe method is an extension method on object. The DLR doesn't allow you to call extension methods on objects typed as dynamic (hence the runtime binder exception you're seeing)

I'd suggest that if you want to call ShouldNotBe(null) on result, you'd have to cast it to an object first (ie: ((object)result).ShouldNotBe(null))

-x

Upvotes: 4

Related Questions