3

I have been tasked with writing a unit test for the following Web API 2 action:

public HttpResponseMessage Get()
{
    IEnumerable<KeyValuePair<long, string>> things = _service.GetSomething();
    return ActionContext.Request.CreateResponse(things.Select(x => new 
        { 
            Thing1 = x.Prop1.ToString(), 
            Thing2 = x.Prop2 
        }).ToArray());
}

I am testing the status code and that works fine, but I have not been able to figure out how I can extract the content data and test it. Here's my test so far:

[TestMethod]
public void GetReturnsOkAndExpectedType()
{
    var controller = GetTheController();
    var response = controller.Get();
    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
    dynamic responseContent;
    Assert.IsTrue(response.TryGetContentValue(out responseContent));
    //???? How can I cast/convert responseContent here ????
}

If I debug the test and inspect responseContent in the immediate window, I see this (I have mocked/stubbed in a single fake value for testing):

{<>f__AnonymousType1<string, string>[1]}
    [0]: { Thing1 = "123", Thing2 = "unit test" }

I can cast this as an array of object, but if I try and extract the values by their property names, I get an error (immediate window again):

((object[])responseContent)[0].Thing1
'object' does not contain a definition for 'Thing1' and no extension method 'Thing1' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

Similarly, if I try to cast and project to an anonymous type of the same shape, it will not compile:

//Thing1 and Thing2 get red-lined here as 'cannot resolve symbol'
var castResult = ((object[]) responseContent).ToList().Select(x => new {x.Thing1, x.Thing2});  

I know I can probably achieve what I want to do if I serialize/deserialize everything using something like JsonConvert, but that doesn't seem like the "right" way to do it. I feel like I'm missing something fundamental, here. How can I cast/convert an anonymous type from HttpResponseMessage for unit testing?

AJ.
  • 16,368
  • 20
  • 95
  • 150
  • 1
    Probably your best option is to either use reflection or cast the result and each item in the array as a dynamic object (In both cases you will need to verify property by property). See [this question](http://stackoverflow.com/questions/16876144/asserting-jsonresult-containing-anonymous-type) – Daniel J.G. Jun 03 '15 at 17:04
  • 2
    If you add `[assembly: InternalsVisibleTo("YourTestAssembly")]` to your controller assembly, then you will be able to write in your test `Assert.AreEqual("Foo", responseContent[0].Thing1);` – Daniel J.G. Jun 03 '15 at 17:23
  • @DanielJ.G. though I like the idea of the `InternalsVisibleTo` attribute, the general idea is not to alter the controller code for testing, if possible. Your other comment using Reflection works. Why don't you post it as an answer so I can upvote/accept? – AJ. Jun 03 '15 at 18:02
  • 1
    I see you preferred the reflection approach :), I know it's not ideal to modify the production code for test purposes but that `InternalsVisibleTo` is added on the AssemblyInfo.cs file of the project containing the controller and the resulting test code looks nicer in my opinion. In any case, both options are valid to test your code! – Daniel J.G. Jun 04 '15 at 08:57
  • @DanielJ.G. I agree. I'm new to this team and I'm trying to be non-invasive to both the code and the team at this point ;-) – AJ. Jun 05 '15 at 15:22

1 Answers1

1

As has been said by @Daniel J.G. in the comments above, one option would be to use reflection to fetch the values of your properties. Since you appear to be using MS testing framework another alternative is to use the PrivateObject class to do some of the reflection work for you.

So, you could do something like this in your test:

var poContent = ((object[])responseContent).Select(x => new PrivateObject(x)).ToArray();

Assert.AreEqual("123", poContent[0].GetProperty("Thing1"));
Assert.AreEqual("unit test", poContent[0].GetProperty("Thing2"));
forsvarir
  • 10,749
  • 6
  • 46
  • 77