0

enter image description here

I am facing issue while casting API response in a unit test case for testing an API call.

I am using below line to compare Response Status code.

Assert.Equal(((int)eResp.Warn), ((ApiResponse)((ObjectResult)response).Value).StatusCode);

I am using below line to compare validation message.

Assert.Equal("Please enter a unique Name.", ((ApiResponse)((ObjectResult)response).Value).Message);

Below line returns response from controller

 return Ok(new { Response = GetWarning(Warningmessage, MessageType) });

GetWarning method returns response as follows

return new ApiResponse((int)HttpStatusCode.Accepted, warningMessage, MessageType);

But I am getting an error of "Invalid cast". Attached is a screenshot of response object

enter image description here

How to cast correctly and solve this issue?

halfer
  • 19,824
  • 17
  • 99
  • 186
Pwavel002
  • 279
  • 2
  • 10
  • This link might help [Link](https://stackoverflow.com/questions/50092716/system-invalidcastexception-while-casting-an-object-type-to-custom-class) – karthickj25 Oct 16 '18 at 13:49
  • Thanks for the link. But it doesn't seem useful in my case. – Pwavel002 Oct 16 '18 at 14:15
  • 1
    Can you add the code for the controller that you are testing? Just the part where you serialize return the `ApiResponse` object should be enough if you can't share it all. Also what is the full exception message e.g. `Unable to cast object of type 'xxx' to 'xxx'`. – Simply Ged Oct 16 '18 at 22:56
  • I have added the exception screenshot and response returned from controller – Pwavel002 Oct 17 '18 at 06:41
  • Thanks. That makes sense now. Also, please note it is always better to paste text into your question instead of images, mostly because search engines can index the question based on the text but not on the contents of the image, which makes it easier for others to find the question (and the answer) :-) – Simply Ged Oct 17 '18 at 22:39

1 Answers1

1

In your controller you are doing this:

return Ok(new { Response = GetWarning(Warningmessage, MessageType) });

This is causing the controller to wrap your response with an extra "Response" object like this:

{
    "response": {
        "statusCode": ...
        "message": ...
        "messageType": ...
    }
}

NOTE: The StatusCode, Message and MessageType fields are the properties on your ApiResponse class.

But when you access the response in your test you are attempting to access it as an ApiResponse but, because of the additional Response field it cannot deserialize it.

The way to fix this is to change your controller return statement to:

return Ok(GetWarning(Warningmessage, MessageType));

This will change the format of the JSON response to:

{
    "statusCode": ...,
    "message": ...
    "messageType": ...
}

Now the return type of your controller method (which is the response.Value property) is an OkObjectResult which means you can modify your tests to require less casting:

Assert.Equal(((int)eResp.Warn), ((ApiResponse)response.Value).StatusCode);
Assert.Equal("Please enter a unique Name.", ((ApiResponse)response.Value).Message);

Now we don't have to cast the response object as it is already anOkObjectResult which has the Value property on it.

EDIT

If you need to keep the { Response: ... } wrapper then you should use a class to do that. I would refactor your existing classes to be something like:

e.g.

public class ApiResponse
{
    public Response Response { get; set; }
}

public class Response
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    public string MessageType { get; set; }
}

And in your controller you would do the following:

return Ok(new ApiResponse{ Response = GetWarning(...) });

This will return the following JSON:

{
    "response": {
        "statusCode": 201,
        "message": "All good"
        "messageType: ...
    }
}

And now you can use the classes we've defined in our tests to validate the controller:

var result = controller.Get();   // this is your controller call

var apiResponse = result.Value as ApiResponse;

Assert.AreEqual(201, apiResponse.Response.StatusCode);
Assert.AreEqual("All good", apiResponse.Response.Message);

NOTE: Adding an extra layer in your controller return method is fine if you are just sending the result to something that handles JSON in an easy way. Unfortunately the C# unit tests need a strong type i.e. a class, to be able to deserialize the response and reference the properties in the test.

Simply Ged
  • 8,250
  • 11
  • 32
  • 40
  • Thanks for your feedback.I have already implemented this previously but I want the the output in "response": { "statusCode": ... "message": ... "messageType": ... } format as it is UI requirement. – Pwavel002 Oct 18 '18 at 04:29
  • OK. You didn't mention that in your original question :-) I've updated my answer to show how you can keep the `Response` part in your code and tests. – Simply Ged Oct 18 '18 at 05:08
  • @Pwavel002 I believe it's an awesome answer and it should be marked as accepted (you can do it as an owner of the question). – Stas Ivanov Jul 28 '19 at 20:23