How to POST the below request using RestAssured in selenium.
The request is as follows:
{
"ShipmentID": "",
"ShipmentNumber": "123455-6",
"Comments": "",
"LineIDs": [
{
"ShipmentDID": "",
"AssetNum": "759585",
"FileC": "",
"SerialN": "",
"LineID": "5",
"Status": "Accept",
"TransferCancelComment": ""
}
Below is the code I have used but not sure how should i continue for the "LineID's" as it has few more attributes in it.
#Test
public void TransferIn() {
RestAssured.baseURI="testurl.rest.com";
RequestSpecification httpRequest = RestAssured.given();
JSONObject requestparams=new JSONObject();
try {
requestparams.put("ShipmentID", "");
requestparams.put("ShipmentNumber", "123455-6");
requestparams.put("Comments", "");
requestparams.put("LineIDs", "");
}
Hope below code will solve your problem.
#Test
public void TransferIn() {
RestAssured.baseURI="testurl.rest.com";
RequestSpecification httpRequest = RestAssured.given();
JSONObject requestparams = new JSONObject();
JSONArray lineIdsArray = new JSONArray();
JSONObject lineIdObject = new JSONObject();
try {
requestparams.put("ShipmentID", "");
requestparams.put("ShipmentNumber", "123455-6");
requestparams.put("Comments", "");
lineIdObject.put("ShipmentDID", "");
lineIdObject.put("AssetNum", "759585");
lineIdObject.put("FileC", "");
lineIdObject.put("SerialN", "");
lineIdObject.put("LineID", "5");
lineIdObject.put("Status", "Accept");
lineIdObject.put("TransferCancelComment", "");
lineIdsArray.put(lineIdObject);
requestparams.put("LineIDs", lineIdsArray);
} catch (JSONException e) {
}
System.out.println(requestparams);
}
A better approach could be, construct the json from a POJO/model file and then pass that to the test. By this, there is clear separation of the intent and in future if you want to verify any response of that type, you can simply de-serialize and get the values using getters of the POJO.
e.g if your json is
{
"name":"Mohan",
"age":21
}
Your POJO would look something like below:
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Example {
#SerializedName("name")
#Expose
private String name;
#SerializedName("age")
#Expose
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
I am using GSON from google which is serialization and de-serialization library. Construct your payload using your POJO and pass that as an argument to your test method.
This will make your code more readable, maintainable, scalable ....
The idea behind this was the intent of test should not be polluted and there will be clear separation between the responsibilities of different entities.
Related
I have asked several basic questions related to this in the past and got great answers that explained several issues. I think i'm now in a position to ask the correct question now that I'm more aware of how Xunit works!
I am trying to parametrize several tests in C# using visual studio. I need each parameter to be displayed as an individual test that can be ran in isolation if required (I know there is a test collection runner and a separate test runner). The test collection runner is my issue.
I know that Xunit requires the parameters to be serialized in order for them to be picked up by the test collection runner. I also know that it by default can easily serialize basic data types like string, bool, int etc.
I have tried various approaches to do this with mixed results. My issue is trying to parameterize the Selenium type 'By'. I can't seem to be able to serialize this. I've tried to trick Xunit for example by using a dictionary List<string, By> and trying to serialize the in the dictionary (no luck!)
Here is the cleanest code I have come across that is simple and elegant for what i'm trying to do, but again I can't serialize the 'By' type. I have played around with changing the static property from bool to By and it returns only 1 test for all params, so it's not being serialized
public class ParamTest1
{
static string test3 = "TestXYZ";
public static TheoryData<int, bool, string, string> DataForTest1 = new TheoryData<int, bool, string, string>
{
{ 1, true, "First", test3 },
{ 2, false, "Second", test3},
{ 3, true, "Third", test3}
};
[Theory(DisplayName = "My First Test"), MemberData(nameof(DataForTest1))]
public void Test1(int valA, bool valB, string valC, string valD)
{
Assert.True(valB);
}
}
Which gives me
I am aware this particular code isn't invoking the IXunitSerializable
So here is an another working example of what I need but I just can't get it to work with the 'By' Type
public class ValidateTestCase : IXunitSerializable
{
public Guid Coupon { get; set; }
public bool IsValid { get; set; }
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(Coupon), Coupon.ToString());
}
public void Deserialize(IXunitSerializationInfo info) { }
}
public class Testing
{
public static IEnumerable<object[]> ValidateTestCases
{
get
{
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("73e4d185-70cf-4ce4-bc3f-187b7a40e167"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("93b983fb-5b6a-4845-a769-db41900b7df9"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("99c03283-33cb-4e56-a010-c2bc0758ad27"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("16a7fe80-3111-44b0-9ebf-c7159bea637d"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("8b38b4aa-d70f-4ce7-8992-8a60936c5c58"), IsValid = false } };
yield return new object[] { new ValidateTestCase { Coupon = Guid.Parse("abc60aa0-a33b-4057-8f99-5cdceda35c70"), IsValid = true } };
}
}
[Theory(DisplayName = "CouponService should validate coupons")]
[MemberData(nameof(ValidateTestCases))]
public void MyCouponService_Validates(ValidateTestCase vtc)
{
Assert.Equal(vtc.IsValid, true);
}
}
And finally for anyone wondering what the 'By' type is I am referring to it's :
[![enter image description here][2]][2]
Here it is in the debugger so you can see what's going on inside:
[![enter image description here][3]][3]
I know there's a lot going on in there but if anyone has any ideas or suggestions it would be great!
To summarize, I can't parameterize the Selenium 'data type' By.
[2]: https://i.stack.imgur.com/XcLcn.png
[3]: https://i.stack.imgur.com/T9so1.png
Serializing Class
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using OpenQA.Selenium;
using Xunit.Abstractions;
namespace XUnitTestProject1
{
public class ParameterizedHook
{
public By p1 { get; set; }
public By p2 { get; set; }
public string assertion { get; set; }
}
public class TheoryWrapper<T> : IXunitSerializable
{
public TheoryWrapper(string label, T #object)
{
Name = label;
Object = #object;
}
public TheoryWrapper()
{
}
public string Name { get; set; }
public T Object { get; set; }
public void Deserialize(IXunitSerializationInfo info)
{
Name = info.GetValue<string>("Label");
Object = JsonConvert.DeserializeObject<T>(info.GetValue<string>("objValue"));
}
public void Serialize(IXunitSerializationInfo info)
{
info.AddValue("Label", Name, typeof(string));
var json = JsonConvert.SerializeObject(Object);
info.AddValue("objValue", json);
}
public override string ToString()
{
return Name;
}
}
}
Working Test
public static IEnumerable<object[]> ComplexTheoryData
{
get
{
return new List<object[]>
{
new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { p1 = nav_hold.pip_builder.dash_expand_pip_builder_menu, p2 = nav_hold.pip_builder.dash_project_stages, assertion = "WORK STAGE" })},
new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { p1 = nav_hold.pip_builder.dash_expand_pip_builder_menu, p2 = nav_hold.pip_builder.dash_project_stages, assertion = "WORK STAGES" })},
//new object[] {0, new TheoryWrapper<ParameterizedHook>("Pass0", new ParameterizedHook { TestData = b.login })},
//new object[] {1, new TheoryWrapper<ParameterizedHook>("Pass1", new ParameterizedHook { TestData = b.password })}
};
}
}
[SkippableTheory]
[Trait("xUnit", "ForTestRunner")]
[MemberData(nameof(ComplexTheoryData))]
public void Test_Navigation(int id, TheoryWrapper<ParameterizedHook> test)
{
nav_met.NavMethodTest(test.Object.p1, test.Object.p2);
By page_title = By.Id("ctl00_lblPageTitle");
Assert.True(nav_met.VerifyText(page_title, test.Object.assertion));
}
I am having some trouble serializing/deserializing my classes below.
My Data class holds a list of other classes.
When I call the serialize/deserialize methods in the Data class, I get the following error:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.amazon.rancor.storage.types.ChildData: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
The error comes from the deserialize method. But I also believe the serialization is not working properly. This is what the serialized Data object looks like:
{childData:[{zipCode:{present:true},countryCode:"US"}]
The Optional field is not being serialized properly even though I have set the objectMapper.registerModule(new Jdk8Module()); field
I can't seem to figure out what I am doing wrong. Maybe I need to change something in ChildData and ChildDataV2 class. But I am not sure what.
Any pointers would be appreciated!
public class Data {
private List<ChildData> childData;
private List<ChildDataV2> childDataV2;
private static ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new Jdk8Module());
}
public Data() { }
#JsonCreator
public Data(#JsonProperty("childData") final List<ChildData> childData,
#JsonProperty("childDataV2") final List<ChildDataV2> childDataV2) {
this.childData = childData;
this.childDataV2 = childDataV2;
}
public List<ChildData> getChildData() {
return childData;
}
public void setChildData(final List<ChildData> childData) {
this.childData = childData;
}
public List<ChildDataV2> getChildDataV2() {
return childDataV2;
}
public void setChildDataV2(final List<ChildDataV2> childDataV2) {
this.childDataV2 = childDataV2;
}
public String serialize() {
try {
return objectMapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to serialize. Data: " + this, e);
}
}
public Data deSerialize(final String data) {
try {
return objectMapper.readValue(data, Data.class);
} catch (IOException e) {
throw new RuntimeException("Failed to deserialize. Data" + data, e);
}
}
}
public class ChildData {
private final String countryCode;
private final Optional<String> zipCode;
public ChildData(final String countryCode, final Optional<String> zipCode) {
this.countryCode = countryCode;
this.zipCode = zipCode;
}
public Optional<String> getZipCode() {
return zipCode;
}
public String getCountryCode() {
return countryCode;
}
}
public class ChildDataV2 extends ChildData {
private final Object struct;
public ChildDataV2(final String cc, final Optional<String> postalCode,
final Object struct) {
super(cc, postalcode);
this.struct = struct;
}
}
The exception is quite clear right? You need to add a default constructor for ChildData or annotate the existing constructor like this:
#JsonCreator
public ChildData(#JsonProperty("countryCode") String countryCode, #JsonProperty("zipCode") Optional<String> zipCode) {
this.countryCode = countryCode;
this.zipCode = zipCode;
}
I want o post data on https://fcm.googleapis.com/fcm/send
along with two headers in Retrofit
Data to be sent
{
"data" : {
"title": "My Title",
"content": "My message"
},
"to": "cKA7LrjBQ6s:APA91bHtY6RBwZ4KZvxbl9VNZMVKz5_NDbE2dP3zgrhJNBSAKDyfOAbfxEi8pnAwc82pzLoGEZImZBv9MXvoBSJy6c0790oqUIYLECCU5WZVcGeSJJNECX5bsLMutYrSPjLSDffP5N3u"
}
It's very simple. Create the following classes.
public interface RestInterface {
#Headers({
"Content-Type: application/json",
"Authorization: key=<YOUR_FCM_SERVER_KEY_HERE>"
})
#POST("fcm/send")
Call<ResponseBody> sendNotification(#Body NotificationBody body);
}
Replace <YOUR_FCM_SERVER_KEY_HERE> with your actual FCM server key.
public class NotificationBody {
#SerializedName("data")
private Data data;
#SerializedName("to")
private String to;
public NotificationBody(Data data, String to) {
this.data = data;
this.to = to;
}
}
Above POJO class will generate outer JSONObject in run-time. And the following POJO class will generate data JSONObject.
public class Data {
#SerializedName("title")
private String title;
#SerializedName("content")
private String content;
public Data(String title, String content) {
this.title = title;
this.content = content;
}
}
And finally use above code in your Activity/Fragment classes like below,
String title = "My Title";
String content = "My message";
String to = "cKA7LrjBQ6s:APA91bHtY6RBwZ4KZvxbl9VNZMVKz5_NDbE2dP3zgrhJNBSAKDyfOAbfxEi8pnAwc82pzLoGEZImZBv9MXvoBSJy6c0790oqUIYLECCU5WZVcGeSJJNECX5bsLMutYrSPjLSDffP5N3u";
Data data = new Data(title, content);
NotificationBody body = new NotificationBody(data, to);
RestInterface api = ....;
Call<ResponseBody> call = api.sendNotification(body);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
// do whatever you want to do
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("TAG", "Error: ", t);
}
});
And don't forget to set Retrofit BASE_URL to https://fcm.googleapis.com/
I have a simple pojo where i have one list of strings and default get/set, i have another get so that in json i get 2 different fields
my pojo and test code snippet are below
public static class TestClass{
public ArrayList<String> names = null;
public ArrayList<String> getNames() {
if(null == names) names = new ArrayList<>();
return names;
}
public void setNames(ArrayList<String> names) {
this.names = names;
}
public ArrayList<String> getNames_r() {
return getNames();
}
#Override
public String toString() {
return "TestClass [names=" + names + "]";
}
}
#Test
public void testDeSerializationSimple() throws JsonParseException, JsonMappingException, IOException{
String justSchool = "{\"names\":[\"second\",\"one\",\"two\",\"three\"],\"names_r\":[\"second\",\"one\",\"two\",\"three\"]}";
ObjectMapper myDefaultMapper= new ObjectMapper();
myDefaultMapper.setDateFormat(CoreUtils.COMMON_SIMPLE_DATE_FORMAT)
.setTimeZone(TimeZone.getTimeZone("UTC"))
.enable(SerializationFeature.INDENT_OUTPUT)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
TestClass testReadDummy = myDefaultMapper.readValue(justSchool, TestClass.class);
System.out.println(" mapper test read = "+testReadDummy);
//assertEquals(testRead.getListString().size(),4);
System.out.println("list = "+testReadDummy);
assertEquals(testReadDummy.names.size(), 4);
}
Try using the #JsonIgnore annotation in the duplicate of your list so it doesn't get serialized, it would look something like:
#JsonIgnore
public ArrayList<String> getNames_r() {
return getNames();
}
That should get rid of your duplicate field in you serialized JSON.
Hope it helps,
Jose Luis
configuring the mapper to not use getters as setters using
MAPPER.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
solved this
I am developing a set of REST web services for my company. We are trying to settle on what technologies we want to use.
I have a template web service that works with JAX-RS Jersey 2.6 when I use the Jackson JSON providers, but doesn't seem to marshal the #QueryParam correctly when I use the Moxy providers.
The search may include multiple "types" such as type=keyword&type=product_number&type=fubar
These are mapped to the List types which contains all of the "type" QueryParam. When I build the project with Jackson, the values of type are correctly collected into the List, when I use MOXy the List is null. MOXy does map all of the other Query and Path Params in the BeanParam.
The problem seems to be in how JERSEY is
When I use Jackson the service works great:
http://XXX:8080/SearchTermJersey/search/1/as/wat?type=product_number&type=keyword&count=4&lang=en_US
This is the JSON it returned:
{"autoSuggestions":{"product_number":{"<span>wat</span>21000":34},"keyword":{"<span>wat</span>er":100,"<span>wat</span>er solution":50,"<span>wat</span>er purity":100}},"language":"en_US","requestDate":1393623225135,"responseDate":1393623225135,"term":"wat","version":"1"}
The URL for the Moxy version of the service returns:
{"language":"en_US","requestDate":1393622174166,"responseDate":1393622174166,"term":"wat","version":"1"}
The Java code is identical between the MOXy and Jackson versions
This is the BeanParam:
public class AutoSuggestParam {
#PathParam("version")
private String version;
#PathParam("term")
private String term;
private List<String>types;
private Integer count;
String language;
public AutoSuggestParam(#QueryParam("count")int count, #QueryParam("type")List<String>types, #QueryParam("lang")String language) {
this.types = types;
this.count = count;
this.language = language;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTerm() {
return term;
}
public void setTerm(String term) {
this.term = term;
}
public Integer getCount() {
return count;
}
public String getLanguage() {
return language;
}
public List<String>getTypes() {
return types != null ? types : new ArrayList<String>();
}
The problem seems to be in how the types parameter is handled. With Jackson the types QueryParams are correctly marshalled into the List, but MOXy fails and just returns a null. So getTypes is returning an empty List. The simple QueryParam count and lang are handled correctly. Is this a bug in Jersey or do I need to do something else with MOXy?
Here is my Resource class:
#javax.ws.rs.Path("/search/{version}/as/{term}")
public class AutoSuggestResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public AutoSuggestResponse getAutoSuggest(#BeanParam AutoSuggestParam autoSuggestParam) {
System.out.printf("Request: term=%s version=%s lang=%s type=%s count=%d%n",
autoSuggestParam.getTerm(),autoSuggestParam.getVersion(), autoSuggestParam.getLanguage(), autoSuggestParam.getTypes().get(0), autoSuggestParam.getCount());
return search(autoSuggestParam);
}
private AutoSuggestResponse search(AutoSuggestParam autoSuggestParam) {
AutoSuggestResponse autoSuggestResponse = new AutoSuggestResponse();
autoSuggestResponse.setRequestDate(new Date().getTime());
autoSuggestResponse.setVersion(autoSuggestParam.getVersion());
autoSuggestResponse.setTerm(autoSuggestParam.getTerm());
autoSuggestResponse.setLanguage(autoSuggestParam.getLanguage());
int cnt = 0;
for (String type : autoSuggestParam.getTypes()) {
if ("product_number".equals(type)) {
Map<String, Object> values = autoSuggestResponse.getAutoSuggestions().get(type);
if (values == null) {
values = new LinkedHashMap<String, Object>();
autoSuggestResponse.getAutoSuggestions().put(type, values);
}
String key = String.format("<span>%s</span>21000", autoSuggestParam.getTerm());
values.put(key, 34);
cnt++;
}
else if ("keyword".equals(type)) {
Map<String, Object> values = autoSuggestResponse.getAutoSuggestions().get(type);
if (values == null) {
values = new LinkedHashMap<String, Object>();
autoSuggestResponse.getAutoSuggestions().put(type, values);
}
String key = String.format("<span>%s</span>er", autoSuggestParam.getTerm());
values.put(key, 100);
cnt++;
key = String.format("<span>%s</span>er solution", autoSuggestParam.getTerm());
values.put(key, 50);
cnt++;
key = String.format("<span>%s</span>er purity", autoSuggestParam.getTerm());
values.put(key, 100);
cnt++;
}
if (cnt >= autoSuggestParam.getCount()) {
break;
}
}
autoSuggestResponse.setResponseDate(new Date().getTime());
return autoSuggestResponse;
}
The Response class:
public class AutoSuggestResponse {
private Long requestDate;
private Long responseDate;
private String version;
private String term;
private String language;
private Map<String, Map<String,Object>>autoSuggestions = new LinkedHashMap<String, Map<String,Object>>();
public Long getRequestDate() {
return requestDate;
}
public void setRequestDate(Long requestDate ) {
this.requestDate = requestDate;
}
public Long getResponseDate() {
return responseDate;
}
public void setResponseDate(Long responseDate) {
this.responseDate = responseDate;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTerm() {
return term;
}
public void setLanguage(String language) {
this.language = language;
}
public String getLanguage() {
return language;
}
public void setTerm(String term) {
this.term = term;
}
public Map<String, Map<String,Object>>getAutoSuggestions() {
return autoSuggestions;
}
}
The web.xml
<display-name>MoxyAS</display-name>
<servlet>
<servlet-name>MoxyAutoSuggest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.sial.search.ws</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<servlet-mapping>
<servlet-name>MoxyAutoSuggest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
By default EclipseLink JAXB (MOXy) will not marshal properties that only have a getter. You can add an #XmlElement annotation to have it become mapped:
#XmlElement
public Map<String, Map<String,Object>>getAutoSuggestions() {
return autoSuggestions;
}
By default MOXy does not use the map key as the JSON key. Below is a link to an example that explains how to set this up:
http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-using-maps-key-as.html
I figured out the problem. It had nothing to do with MOXy, adding the genson-0.98.jar to the path fixed the problem with the QueryParam not getting marshaled in the BeanParam.
Adding the #XmlElement to the resource did make Moxy work, sort of. If I add XmlElement to the Map in the Response Class I now get an error:
javax.servlet.ServletException: org.glassfish.jersey.server.ContainerException: java.lang.NoClassDefFoundError: org/eclipse/persistence/internal/libraries/asm/ClassWriter
But this is a new problem.