On my endpoint's response I need to omit a property if its value is null , so I have tagged the prop with the [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] tag.
So, with the tag on the property, this property is not going to be part of the response payload and that's what I want to check/assert on my unit tests, that the property does not appear on my JSON response.
I'm using FluentAssertions as assertion framework and AutoFixture as mock generator.
Why not just assert the response of the HTTP request against a raw JSON? Or use something like where you deserialize the JSON into an object which structure is defined by an anonymous type. See this example on how this works.
I serialized the response of the endpoint, parsed it to a JObject and then validated if the token exists inside that json, something like this:
//Act
var result = await controller.Get(entity.Id);
var objectResult = result as OkObjectResult;
string serializeResponse = JsonConvert.SerializeObject(objectResult.Value,Formatting.Indented);
JObject responseJOject = JObject.Parse(serializeResponse);
//Assert: this is how I checked if the tokens were or not on the json
using (new AssertionScope())
{
responseJOject.ContainsKey("myToken1").Should().BeFalse();
responseJOject.ContainsKey("myToken2").Should().BeFalse();
}
This was based on #dennis-doomen response, I took his advice, but in the opposite way, because all I wanted is to validate if the property was or not inside the response payload (json "body").
Related
I am currently receiving the following JSON response from my api:
{"Lastname":["ERRLASTNAMEEMPTY"],"Firstname":["ERRFIRSTNAMEEMPTY"]}
Note that the above response is dynamic - i.e sometimes I can have FirstName, sometimes LastName, sometimes both. This response is based on the validation of data.
My question is - is there a way to deserialize this response using JsonSerializer.DeSerialize?
I have tried to use it like this but it does not work:
var errorBody = JsonSerializer.Deserialize<dynamic>(body, serializerOptions);
JsonSerializer.Deserialize<Dictionary<string,string[]>>(body, serializerOptions);
You can work with dynamic JSON objects with JObject like that:
var data = JObject.Parse(body);
And later you are able to access values with data["Lastname"]?.Value<string>(); and such, the way you want.
JsonSerializer.Deserialize<ExpandoObject>(body, serializerOptions);
// introduce a dynamic object from a string :
// {"UrlCheckId":581,"Request":{"JobId":"a531775f-be19-4c78-9717-94aa051f6b23","AuditId":"b05016f5-51c9-48ba-abcc-ec16251439e5","AlertDefinitionId":108,"JobCreated":"2022-05-20T07:09:56.236656+01:00","UrlJobId":0,"BatchJobId":"e46b9454-2f90-407d-9243-c0647cf6502d"},"JobCreated":"2022-05-20T07:10:21.4097268+01:00"}
// ...The UrlCheckId is an int primitive and Request is our UrlCheckJobRequest object.
dynamic msg = JsonConvert.DeserializeObject(message);
// to access an int (primitive) property:
int id = msg.UrlCheckId;
// to access an object, you might have to serialize the request into a string first...
var r = JsonConvert.SerializeObject(msg.Request);
// ... and then deserialize into an object
UrlCheckJobRequest request = JsonConvert.DeserializeObject<UrlCheckJobRequest>(r);
I have a string value:
var responseString = {"ErrorType":"ServerError","Message":"Incoming data error.","Properties":null}
When I call JObject.Parse(responseString);, I get the following dynamic object:
{{
"ErrorType": "ServerError",
"Message": "Incoming data error.",
"Properties": null
}}
Why is JObject creating a dynamic object that is an object wrapped in an object? I was hoping to write code to access the Message property such as responseMessage.Message as string, but that throws an error.
I just tried the following code in LinqPad:
var responseString = "{\"ErrorType\":\"ServerError\",\"Message\":\"Incoming data error.\",\"Properties\":null}";
dynamic responseMessage = JObject.Parse(responseString);
var msg = (string) responseMessage.Message;
msg.Dump();
In the output, I get the desired Incoming data error. string, so it looks like it is doing what it should. How does your code look? And what version of Json.NET are you using? Also, it is NOT possible to use as to convert to string, as this will return null, since the value is a JToken. You need the explicit cast.
is it possible to test many cases for example:
[TestCase(""), TestCase(null), TestCase("String"), TestCase("2010-07-14T00:00:00.000Z"), TestCase("201-07-14T00:00:00.000Z")]
And when I am getting response from API with deserialized json to my Error object I assume that Response Body Message and HttpStatusCode is correct after different VALIDATIONS from API and it will be correct in from my assert.
Method where I am deserializing from Json to generic type:
public TType GetResponseJsonAsObject<TType>(HttpResponseMessage content)
{
var contentResult = content.Content.ReadAsStringAsync().Result;
var specialCase = JsonConvert.DeserializeObject<TType>(contentResult);
return specialCase;
}
Here is my test method:
[TestCase(""), TestCase(null), TestCase("String"), TestCase("2010-07-14T00:00:00.000Z"), TestCase("201-07-14T00:00:00.000Z")]
public void PostAPI_SpecialCaseRequestDate_WithError_ProceedsSuccessfully(string date)
{
// arrange
SpecialCaseRequest data = new SpecialCaseRequest()
{
Name = "Test",
ExpirationDateTime = date,
};
// act + assert
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
PostMethods sendJsonDemo = new PostMethods();
var response = sendJsonDemo.SendJsonDemo(json);
var responseBody = sendJsonDemo.GetResponseJsonAsObject<Error>(response);
Assert.IsNotNull(responseBody);
Assert.AreEqual("Date parameter cannot be empty string", responseBody.Message);
Assert.AreEqual("Date parameter cannot be empty null", responseBody.Message);
}
Idea is to test all cases in one method and pass them successfully, but in this case it will fail on second Assert, because after validation from API response body message have this string: "Date parameter cannot be empty string" and first Assert will be passed, but it will fail on second Assert, because responseBody.Message from api is : "Date parameter cannot be empty string". Same would be with othe TestCases such as null or "String" and etc, because from TestCase(null) , it will fail on first assert because after validation from api response body comes with different string responseBody.Message = "Date parameter cannot be empty null" . How can I manage these Asserts nicely without creating separate test methods for each TestCase ?
You can pass two parameters for test. One is date and the second can be expectedError. This way your TestCases would look like:
[TestCase("", "Error1"), TestCase(null, "Error2"), TestCase("String", "Error3")]
[TestCase("2010-07-14T00:00:00.000Z", "Error4")]
[TestCase("201-07-14T00:00:00.000Z", "Error5")]
And you will have just a single Assert:
Assert.AreEqual(expectedError, responseBody.Message);
Following the clarification that OP is using NUnit 3.5.0 I think you should take a look at the TestCaseData attribute:
https://github.com/nunit/docs/wiki/TestCaseData
Looks like it provides an even better code organization possibility than the solution proposed by #DovydasSopa, though the single assert stays the same.
I have this object and would like to filter it based on the value of customer_email. For example, I only want to return commissions where the customer email is test#test.com. This is a simplified example of what a response would look like. Below is the method where I grab all the data.
public RootObject GetData(string customerEmail)
{
var data = new Commission();
using (var httpClient = new HttpClient())
{
var requestContent = JObject.FromObject(new
{
commission_id = data.id,
customer_email = customerEmail
}).ToString();
......
RootObject response = JsonConvert.DeserializeObject<RootObject>(responseContent);
return response;
}
}
When I get the response, the information looks like this:
{"response":{
"code":"200",
"message":"OK: The request was successful. See response body for additional data.",
"data":{
"commissions":
[{
"commission_id":"12345",
"customer_email":"test#test.com"
},
{
"commission_id":"67890",
"customer_email":"fake#fake.com"
}]
You can achieve it using simple LINQ right after serialization of response to RootObject.
return response
.Response.Data.Commissions
.Where(commission => commission.CustomerEmail == customerEmail);
Then you can use a lambda expression to easily filter the contents of that list.
return response.Response.Data.Commissions.Where(c => c.customerEmail==customerEmail);
This will return only the instances of Commission in the response's commissions collection.
A side effect of this solution would be changing the return type of the method from RootObject to IEnumerable<Commission>. Which would mean that callers of this method would no longer have access to any other data in the response object (besides the commissions data).
I'm receiving a JSON string in a MVC4/.NET4 WebApi controller action. The action's parameter is dynamic because I don't know anything on the receiving end about the JSON object I'm receiving.
public dynamic Post(dynamic myobject)
The JSON is automatically parsed and the resulting dynamic object is a Newtonsoft.Json.Linq.JContainer. I can, as expected, evaluate properties at runtime, so if the JSON contained something like myobject.myproperty then I can now take the dynamic object received and call myobject.myproperty within the C# code. So far so good.
Now I want to iterate over all properties that were supplied as part of the JSON, including nested properties. However, if I do myobject.GetType().GetProperties() it only returns properties of Newtonsoft.Json.Linq.JContainer instead of the properties I'm looking for (that were part of the JSON).
Any idea how to do this?
I think this can be a starting point
dynamic dynObj = JsonConvert.DeserializeObject("{a:1,b:2}");
//JContainer is the base class
var jObj = (JObject)dynObj;
foreach (JToken token in jObj.Children())
{
if (token is JProperty)
{
var prop = token as JProperty;
Console.WriteLine("{0}={1}", prop.Name, prop.Value);
}
}
EDIT
this also may help you
var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(jObj.ToString());