I have a SpecFlow test definition set up where I want to assert that the response have a specific collection of fields in its response. For example I have this particular expected response from the API:
{
isActive: false,
lastProcessed: "2020-11-03T19:03:16.537"
}
What I want to verify is that the response contains those two fields, not necessarily the value those fields contain. I tried the following method:
Assert.NotNull(response.Model.isActive);
Assert.NotNull(response.Model.lastProcessed);
But I'm getting an error using this method:
Do not use Assert.NotNull() on value type 'bool'
How else can I make sure the response structure is as expected other than using "NotNull()"?
The Solution:
Following the accepted answer, I serialized the model returned from the API call into JSON and parsed it into a JObject. Then I used the ContainsKey() method to assert it.
JObject jObject = JObject.Parse(JsonConvert.SerializeObject(response.Model));
Assert.True(jObject.ContainsKey("isActive"));
I don't know what packages you use for sending requests and deserialization but if you could get the response content as a raw json string you could then use Newtonsoft.JSON to parse the response into a JObject with JObject.Parse(responseContent). JObject has a method called ContainsKey(propertyName) which determines whether there is a field of a specified name in the object. You could assert if it returns true for the desired property names.
Edit
Regarding Greg's answer below. The original error is in fact caused by bool not being a nullable type and making it nullable in the model would fix the error. However, this solution is not ideal. In some cases null can be a valid value returned by the API and this would generate false negatives. e.g. if we recieved:
{
isActive: null,
lastProcessed: "2020-11-03T19:03:16.537"
}
then Assert.NotNull(response.Model.isActive) would yield a negative test result even though the field is present in the json, and that's what we wanted to check.
So theoretically if we are 100% sure that null will never be returned by the API itself, then we could do it that way, but it won't be a universal method. Also not very descriptive of what we are trying to achieve ;)
Since the isActive property is a bool you'll need to assert that it is false. If instead you want a true or false value, and then something to represent that it is missing, use a nullable boolean instead in your DTO:
public class YourDTO
{
public bool? isActive { get; set; }
...
}
Then you can assert isActive is null, true or false.
Alternative: If you cannot update the original data transfer object, then this might be a good use case for writing your own code to call the web service and map the JSON response to your own DTO used just for your tests.
This could be a large amount of work, however. The advantage is your test code is truly decoupled from the code it tests. I've done this with applications that use a database as well. It is extra work, but it allows your tests to use whatever data structure makes sense for the test.
Related
I wrote the following method to try to diagnose a problem I'm experiencing in my application:
[Route("/api/flow/test"), HttpPost]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Test(string id, [FromBody] JToken input)
{
var result = input == null ? "Not OK" : "OK";
return Ok(result);
}
I post a large (6.5MB+) JSON body to it, and in one instance it works fine. When I post a similar JSON with some added properties, it does not - the input parameter comes in as null. However, both JSON validate successfully with every tool I've found that can handle their size. Please provide some additional ideas about how to further investigate what is causing the body parameter input to be treated as null.
One option would be to declare it as string instead of JToken and try to explicitly parse it in the action body.
This will show you two things:
If input is still empty, it's not a problem with JSON parsing
If parsing it explicitly fails, the JSON is indeed invalid.
Only when input is not null and parsing it in the body works - only then you need to deep dive into what actually happens, when you declare an action parameter as JToken.
I just took over a fairly large project. One of my tasks is to modify the code that gets rate quotes from FedEx. Currently, in order to get a set of rate quotes for a shipment that includes quotes for each "ServiceType" (Ground, 2-day, overnight, etc.) the code makes one call for each type. FedEx offers a web service that is used to get this information. After doing a little research, it looks like this web service can return multiple ServiceType quotes with a single round-trip. To do this, I'm supposed to "leave the service type out of the request." (Here's the question that pointed me in that direction.)
So I know that I can exclude the service type property from the serialization by decorating the property with, [System.Xml.Serialization.XmlIgnoreAttribute()]. But how can I do that only when I want results for all the ServiceType values and still be able to pass a single service type for the cases where I know what shipping method the user wants?
EDIT: FedEx's documentation indicates that this field is optional. Some basic testing shows that excluding it from the request with the XmlIgnoreAttribute does return data for multiple ServiceTypes.
If you implement a public bool ShouldSerializeXXX() function alongside the XXX property being serialized, XmlSerializer will ignore the corresponding XXX property when the function returns false. You'll have to have some basis for setting this (maybe the XXX property can be null? or you can grab some other state to make the decision.)
So, something along these lines:
public class MyClass
{
public ServiceType? ServiceType { get; set; }
public bool ShouldSerializeServiceType() { return ServiceType.HasValue; }
}
I'm building a Foursquare app for Windows Phone 7. It's working nicely with HttpWebRequests and I'm receiving JSON objects perfectly. I'm using the DataContractJsonSerializer(type) method. But when deserializing this JSON to the object I want, It all goes well until I there's and array of objects, where I don't know which object-type to expect (also don't know the amount & order of objects). But I do need to define this type when deserializing in the method DataContractJsonSerializer(type).
Let me show you a snapshot of JSON which causes the trouble:
notifications":
[{
"type":"notificationTray",
"item":{
"unreadCount":0
}
},{
"type":"message",
"item":{
"message":"OK! We've got you # Strandslag 4. You've been here 2 times."
}
}]
So I'm deserializing this to an array of Notification objects, with two memers: "public String type" and an "public Object item". I can't know which, and how many, Notification's there will be in the array (and thus of what type the 'item' member must be). As you can see in the JSON, the first notification has an item of type int, but the second notification's item is of type string. But I need to know that for the deserialization... Or else it just stays null.. So I cant cast it to the correct type..
Does anyone knows how to solve this nicely?
Hmmm not 100% sure this will answer your question but can you not use javascript to convert whatever the object is into a string and then manipulate it that way?
Something like:
var value = whatever[number].toString;
This would give you a string you can work with. You could then try to parse it back to an int.
var valueInt = parseInt(value);
Then use an IF statement to see if the valueInt exists or not to determine what kind of value the first object has.
Is there a way in MVC3 to set what properties the Json function outputs?
ie. properties on my model have an attribute that tells the Json function not to output them.
It looks like the ScriptIgnoreAttribute will do what you want. Just decorate whatever property you don't want serialized with it.
Use anonymous method for that:
so instead of
return Json(it);
do
return Json(new {
it.Name,
CreatedAt = it.CreatedAt.ToString("D")
// And so on...
});
this way you explicitly publish (map) set of attributes to the web which ensures that only allowed properties can be accessed from JSON.
If you don't want to Repeat Yourself, you can use JSON.NET serializer with which you can customise how objects are serialised. (So you can create custom HideAttribute and take that into account).
With JSON.NET you will also need to write Controller.Json method replacement (SmartJson or so). But it should not be an issue I suppose.
I have a working java client that is communicating with Google, through ProtoBuf serialized messages. I am currently trying to translate that client into C#.
I have a .proto file where the parameter appId is an optional string. Its default value in the C# representation as generated by the protobuf-net library is an empty string, just as it is in the java representation of the same file.
message AppsRequest {
optional AppType appType = 1;
optional string query = 2;
optional string categoryId = 3;
optional string appId = 4;
optional bool withExtendedInfo = 6;
}
I find that when I explicitly set appId to "" in the java client, the client stops working (403 Bad Request from Google). When I explicitly set appId to null in the java client, everything works, but only because hasAppId is being set to false (I'm uncertain as to how that affects the serialization).
In the C# client, I always get 403 responses. I don't see any logic behind the distinction between not setting a value, and setting the default value, that seems to make all the difference in the java client. Since the output is always a binary stream, I am not sure if the successful java messages are being serialized with an empty string, or not serialized at all.
In the C# client, I've tried setting IsRequired to true on the ProtoMember attribute, to force them to serialize, and I've tried setting the default value to null, and explicitly set "", so I'm quite sure I've tried some configuration where the value is being serialized. I've also played around with ProtoBuf.ProtoIgnore and at some point, removing the appId parameter altogether, but I haven't been able to avoid the 403 errors in C#.
I've tried manually copying the serialized string from java, and that resolved my issues, so I'm certain that the rest of the HTTP Request is working, and the error can be traced to the serialized object.
My serialization is simply this:
var clone = ProtoBuf.Serializer.DeepClone(request);
MemoryStream ms = new MemoryStream(2000);
ProtoBuf.Serializer.Serialize(ms, clone);
var bytearr = ms.ToArray();
string encodedData = Convert.ToBase64String(bytearr);
I'll admit to not being quite sure about what DeepClone does. I've tried both with and without it...
It sounds like we want to force it to be excluded; for a first thing to try, you could try using the "detectmissing" option in the code-generation. This is possible from the IDE and command-line, but differently (let me know which you are using and I'll add more).
Another similar option is to add (in a partial class) a bool {memberName}Specified {get;set;}. There is an existing open report of an oddity involving default empty strings, that I am looking at.