Search code examples
c#.netmockingmstest

MS Test with FromBody parameter Web API


I have a C# .Net Web API that I am writing and trying to build a MSTest for one method.

Controlller

     public IHttpActionResult BlahMethod([FromBody] dynamic data)
        {
                string id = data.id;
                List<Property> Props = JsonConvert.DeserializeObject<List<Property>>(data.properties.ToString());
                User existing = _context.MyClass.Where(x => x.Properties.Any(y => y.PropertyLabel == "app" && y.PropertyValue == id)).FirstOrDefault();
                    return Json(existing);
        }

I am trying to test this method using a mock similar to below

Test Method

[TestMethod]
public void AllUsersTest()
{
    var mockSet = new Mock<DbSet<MyClass>>();
    var mockContext = new Mock<DBContext>();
    mockContext.Setup(m => m.MyClass).Returns(mockSet.Object);

    MyController controller = new MyController();
    List<Property> Props = new List<Property>() {
        new Property(){ PropertyLabel = "Nickname", PropertyValue = "TMoney"}
    };
    dynamic data = new TestObject("id", Props);
    var x = controller.BlahMethod(data);
    Assert.IsTrue(true);
}

Classes

public class TestObject {
    public TestObject() { }
    public TestObject(string id, List<Property> props)
    {
        this.id = id;
        this.properties = props;
    }
    public string id { get; set; }
    public List<Property> properties { get; set; } = new List<Property>();

}    
public class Property{
     public string PropertyLabel {get; set;}
     public string PropertyValue {get; set;}
  }

I seem to be having an issue with a JsonReaderException. For some reason there the error is saying that there is an unexpected character. I have also tried sending a dynamic object through and that also caused an issue. Perhaps someone has some advice on the best approach to mocking with APIs in .net using posted data?


Solution

  • The error is the result of the following line in your controller:

    List<Property> Props = JsonConvert.DeserializeObject<List<Property>>(data.properties.ToString());
    

    In this line, you first convert the properties to a string and then try to deserialize the string. However, converting to a string is not the same as serializing as JSON. So the call to DeserializeObject receives an invalid string and throws the error.

    In Web API, you can define the model of the data that you receive as classes and the framework will handle the deserialization for you. So you can simply define a class in your project that comprises all the required properties. Basically you could move the TestObject class from the test project to the Web API project and implement the action as follows:

    public IHttpActionResult BlahMethod([FromBody] TestObject data)
    {
      string id = data.id;
      var Props = data.properties;
      User existing = _context.MyClass.Where(x => x.Properties.Any(y => y.PropertyLabel == "app" && y.PropertyValue == id)).FirstOrDefault();
      if (existing == null)
        return NotFound();
      return Ok(existing);
    }
    

    The changed action does define the required input data by using TestObject as a parameter. If you call the action with JSON, the Web API framework will convert it for you before calling your action.
    In the method, you work directly on the data and return the existing user as object also. The Web API framework takes the return parameter and serializes it so that the caller can understand the format (usually into JSON).

    As for your unit test, you create a sample TestObject as you already did and set the properties as required in your test case. You call the action directly with the data.