Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you unit test ASP.NET Core MVC Controllers that return anonymous objects?

I'm having trouble unit testing ASP.NET Core MVC controllers that return anonymous objects. The unit testing is set up in a separate project and calls the controller methods from the main project directly.

The controller methods return IActionResult but typically these are OkObjectResult and BadRequestObjectResult objects that get translated into a JSON response with the appropriate HTTP status code. The anonymous objects are passed as the constructor parameters for the ObjectResult objects and it is these I'm trying to make assertions against (accessible via ObjectResult.Value).

I found this question (how can i access internals in asp.net 5) that has an answer that says to use dynamics and add

[assembly: InternalsVisibleTo("Namespace")]

to AssemblyInfo.cs to allow the test project access to the internal object properties of the anonymous objects. However, latest versions of ASP.NET Core MVC do not have AssemblyInfo.cs and adding one as suggested in the answers to the linked question does not work either.

Is there now a different location to add the InternalsVisibleTo or am I missing something?

like image 369
Jargon Avatar asked Jul 18 '16 14:07

Jargon


People also ask

Should you write unit tests for controllers?

Controller logic can be tested using automated integration tests, separate and distinct from unit tests for individual components. -1: A unit test for a controller could be pointless.

Can you unit test controllers?

When unit testing controller logic, only the contents of a single action are tested, not the behavior of its dependencies or of the framework itself. Set up unit tests of controller actions to focus on the controller's behavior. A controller unit test avoids scenarios such as filters, routing, and model binding.

Can a unit test be performed on an MVC application without running the controllers in an ASP NET process?

In an MVC application, all the features are based on interface. So, it is easy to unit test a MVC application. And it is to note that, in MVC application there is no need of running the controllers for unit testing.


1 Answers

Original idea from this answer with a more generic approach. Using a custom DynamicObject as a wrapper for inspecting the value via reflection there was no need to add the InternalsVisibleTo

public class DynamicObjectResultValue : DynamicObject, IEquatable<DynamicObjectResultValue> {
    private readonly object value;

    public DynamicObjectResultValue(object value) {
        this.value = value;
    }

    #region Operators
    public static bool operator ==(DynamicObjectResultValue a, DynamicObjectResultValue b) {
        // If both are null, or both are same instance, return true.
        if (System.Object.ReferenceEquals(a, b)) {
            return true;
        }
        // If one is null, but not both, return false.
        if (ReferenceEquals((object)a, null) || ReferenceEquals((object)b, null)) {
            return false;
        }
        // Return true if the fields match:
        return a.value == b.value;
    }

    public static bool operator !=(DynamicObjectResultValue a, DynamicObjectResultValue b) {
        return !(a == b);
    }
    #endregion

    public override IEnumerable<string> GetDynamicMemberNames() {
        return value.GetType().GetProperties().Select(p => p.Name);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        //initialize value
        result = null;
        //Search possible matches and get its value
        var property = value.GetType().GetProperty(binder.Name);
        if (property != null) {
            // If the property is found, 
            // set the value parameter and return true. 
            var propertyValue = property.GetValue(value, null);
            result = propertyValue;
            return true;
        }
        // Otherwise, return false. 
        return false;
    }

    public override bool Equals(object obj) {
        if (obj is DynamicObjectResultValue)
            return Equals(obj as DynamicObjectResultValue);
        // If parameter is null return false.
        if (ReferenceEquals(obj, null)) return false;
        // Return true if the fields match:
        return this.value == obj;
    }

    public bool Equals(DynamicObjectResultValue other) {
        // If parameter is null return false.
        if (ReferenceEquals(other, null)) return false;
        // Return true if the fields match:
        return this.value == other.value;
    }

    public override int GetHashCode() {
        return ToString().GetHashCode();
    }

    public override string ToString() {
        return string.Format("{0}", value);
    }
}

Assuming the following controller

public class FooController : Controller {

    public IActionResult GetAnonymousObject() {

        var jsonResult = new {
            id = 1,
            name = "Foo",
            type = "Bar"
        };

        return Ok(jsonResult);
    }

    public IActionResult GetAnonymousCollection() {

        var jsonResult = Enumerable.Range(1, 20).Select(x => new {
            id = x,
            name = "Foo" + x,
            type = "Bar" + x
        }).ToList();

        return Ok(jsonResult);
    }
}

Tests could look like

[TestMethod]
public void TestDynamicResults() {
    //Arrange
    var controller = new FooController();

    //Act
    var result = controller.GetAnonymousObject() as OkObjectResult;

    //Assert
    dynamic obj = new DynamicObjectResultValue(result.Value);

    Assert.IsNotNull(obj);
    Assert.AreEqual(1, obj.id);
    Assert.AreEqual("Foo", obj.name);
    Assert.AreEqual(3, obj.name.Length);
    Assert.AreEqual("Bar", obj.type);
}

[TestMethod]
public void TestDynamicCollection() {
    //Arrange
    var controller = new FooController();

    //Act
    var result = controller.GetAnonymousCollection() as OkObjectResult;

    //Assert
    Assert.IsNotNull(result, "No ActionResult returned from action method.");
    dynamic jsonCollection = result.Value;
    foreach (dynamic value in jsonCollection) {
        dynamic json = new DynamicObjectResultValue(value);

        Assert.IsNotNull(json.id,
            "JSON record does not contain \"id\" required property.");
        Assert.IsNotNull(json.name,
            "JSON record does not contain \"name\" required property.");
        Assert.IsNotNull(json.type,
            "JSON record does not contain \"type\" required property.");
    }
}
like image 67
Nkosi Avatar answered Sep 21 '22 12:09

Nkosi