Const collection for [Test, Pairwise] - c#

Using c#, nunit, selenium for automation. I would like to use attribute [Test, Pairwise] for my test case to verify that object can be posted with any valid value. I have dictionary with all valid values, but [Values()] - requires const as parameter and ReadOnlyCollection(as it was suggested here) doesn't work for it.
I'm having error: An attribute agrument must be a constant expressiom, typeof expression or array expression of an attribute parameter type.
class ObjectBaseCalls : ApiTestBase
{
static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });
[Test, Pairwise]
public void ObjectCanBePostedAndGeted([Values(AllTypes)] string type)
{
//My test
}
}

There are two different problems here.
The error "An attribute argument must be a constant expression, typeof expression or array expression of an attribute parameter type" is coming from the compiler. It describes a limitation of any attribute constructors in .NET, not just NUnit's. If you want to pass arguments in the constructor itself, then you must use constant values.
However, it seems you don't want to pass the args in the constructor. Instead, you would like to refer to a list declared separately. NUnit has a set of attributes for doing exactly that. You should use one of them, for example, ValueSourceAttribute...
public class ObjectBaseCalls : ApiTestBase
{
static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });
[Test]
public void ObjectCanBePostedAndGeted([ValueSource(nameof(AllTypes))] string type)
{
//My test
}
}
Alternatively, since you only have a single argument to the method, youcould use TestCaseSourceAttribute...
public class ObjectBaseCalls : ApiTestBase
{
static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });
[TestCaseSource(nameof(AllTypes))]
public void ObjectCanBePostedAndGeted(string type)
{
//My test
}
}
Either of these should work. Which one you use here is a matter of stylistic preference.
The second problem is with your use of PairWiseAttribute. It is used (along with several other "combining attributes" when you have a test with multiple parameters specified using Values or ValueSource and you want NUnit to combine them in various ways. In the situation with a single parameter, it does nothing. That's why I removed it from my examples above.
If you actually intended to write a test with more than one parameter and have NUnit decide how to combine them, I think you need to ask a different question where you explain what you are trying to accomplish.

I found the solution and it works for me.
I created enum with parameter name and dictionary with parameter name and parameter value for each object parameter and use enum into my object constructor PairWiseAttribute.
public class MyElement : BaseElement
{
public enum Types { Type1, Type2, Type3, Type4}
public Dictionary<Types, string> AllTypes = new Dictionary<Types, string>()
{
{ Types.Type1, "Value 1" },
{ Types.Type2, "Value 2" },
{ Types.Type3, "Value 3" },
{ Types.Type4, "Value 4" },
};
public enum Category { Category1, Category2, Category3, Category4}
public Dictionary<Category, string> Categories = new Dictionary<Category, string>()
{
{ Category.Category1, "Value 1" },
{ Category.Category2, "Value 2" },
{ Category.Category3, "Value 3" },
{ Category.Category4, "Value 4" },
};
public MyElement(Types type, Category category)
{
type = AllTypes[type];
category = Categories[category];
}
}
public class Test
{
[Test, Pairwise]
public void ValidBaseCheckElementCalls
(
[Values(Types.Type1, Types.Type2, Types.Type3, Types.Type4)] Types objType,
[Values(Category.Category1, Category.Category2, Category.Category3, Category.Category4)] Category objCategory,
)
{
MyElement element = new MyElement(objType, objCategory);
}
}

Related

Best Way to Retrieve Actual Concrete Object Of a Certain Type From a Json-String in C#?

I'm currently struggling with determining which types a json-string deserializer gives back in C#. I've tried to make a base TypeWHATEVER class where TypeX and TypeY inherited from them, but still whenever I try to retrieve the actual object with that specific type, it doesn't work since both can create a new instance with the same constructor.
Let's say I have TypeX and TypeY. They both have similar constructors, for example: both have a constructor with parameters (string a, string b). However, TypeY has an additional constructor with parameters, for example (string a, string b, string c).
Now whenever I use the additional constructor or even just the similar constructor in TypeY, it still doesn't produce the results I want (It still sees it as TypeX as well as TypeY)...
In one of the methods I don't know which type to return, because the object is being deserialized from a Json-String. Also putting T and on the method that gives the object backs works, but it works for both classes which isn't what I want... I even put a enum in each different underlying Type classes, but that didn't work since the Type is defined in the base class as well.
How or what is the best way to determine how to get the actual object type from a json string where there are similar constructors, but still they differ from each other...
Please help me through this :(
Below you'll find sample code of what I'm trying to achieve..
Like I said earlier I have a base class or interface doesn't matter and two child classes
Base Class: E.g. Parent.cs
public class Parent
{
public virtual ClassType Type { get; protected set; } = ClassType.Undefined;//this is an enum and used to differentiate between the different classes. However, this doesn't work, because of the parent type will always be Undefined whenever a child type is converted from json-string to explicit Parent type...
public string Id { get; set; }
public string Message { get; set; }
public Parent()
{
}
public Parent(string id, string message = "")
{
Id = id;
Message = message;
}
}
Child Class: E.g. ChildA.cs
public class ChildA : Parent
{
public override ClassType Type => ClassType.Default;
public ChildA()
{
}
public ChildA(string id, string message = "") : base(id, message)
{
}
}
Child Class: E.g. ChildB.cs
public class ChildB : Parent
{
private object testObjectA;
private object testObjectB;
private object testObjectC;
public override ClassType Type => ClassType.New;
public object TestObjectA
{
get { return testObjectA; }
set { testObjectA = value; }
}
public object TestObjectB
{
get { return testObjectB; }
set { testObjectB = value; }
}
public object TestObjectC
{
get { return testObjectC; }
set { testObjectC = value; }
}
public ChildB()
{
}
public ChildB(string id, string message) : base(id, message)
{
}
public ChildB(string id, IDictionary<string, object> collection, string message = "") : this(id, message) // should I use 'this' or 'base' here for id and message, because I already get the base class for the second constructor and it makes sense to me just to take this class since the other is already been using the base class?
{
testObjectA = collection[Constants.A];
testObjectB = collection[Constants.B];
testObjectC = collection[Constants.C];
}
}
Json Converter Class: E.g. JsonConverterClass.cs
public static T StringToObject<T>(string json)
=> JsonConvert.DeserializeObject<T>(json);//using Newtonsoft.Json;
Now I have a Json-String that I want to convert to either a ChildA or ChildB. The Json-String is something like this:
{
"type": "New",//This is the ClassType for example
"id": "Some String...",
"message": "",
"collection": {
"objectA": "A",
"objectB": "B",
"objectC": "C",
}
}
Let's try to convert the Json-string which won't work and give that Child object back in a method, unfortunate this doesn't work:
public Parent GetObjectExample(string json_String)
{
Parent parentClass = JsonConverterClass.StringToObject<Parent>(json_String);// I don't know how I maybe can use reflection here to accomplish, maybe this is an option too?
switch (parentClass.Type)
{
case ClassType.Default:
parentClass = parentClass as ChildA;
break;
case ClassType.New:
parentClass = parentClass as ChildB;
break;
}
return parentClass;
}
The problem here is that I expect ChildB to given back. However, since both classes have the same constructor. It doesn't recognizes to give ChildB back. In fact, it just gives a random class back.. Either ChildA or ChilB and in most cases it just gives ChildA back what's really weird.
Hopefully, I could be clear as possible to inform you about what is going on and I really don't know why my approach doesn't work...
Ok, I think there are many things to comment. Let's begin:
In your constructor with 3 parameters, use this instead of base. Imagine this situation:
public ChildB(string id, string message)
: base(id, message)
{
this.FullName = $"{id} {message}";
}
public ChildB(string id, IDictionary<string, object> collection, string message = "")
: this(id, message)
{
}
Calling this, your 3 params constructor run the 2 params constructor setting the FullName property. If you use base, FullName won't be initialized and you must duplicate this line in your 3 params constructor.
C# know the type of any object. Really, you don't need your ClassType property. You can do things like:
if (myChildBInstance.GetType() == typeof(ChildB))
I recomend you remove this property.
Also, that property is protected. You can't change it's value from serialization. Serialization (by default) works with public properties.
The key in your question, I think, is with the constructors. When you serialize, you use always the parameterless constructor. In the deserialization, constructor without parameters is invoked and later, the properties are setted.
Your Type property in the JSON string is never used. You can't set the value because is protected and your instance is created before you start processing these properties.
var json = #"{
'type': 'New',//This is the ClassType for example
'id': 'Some String...',
'message': '',
'collection': {
'objectA': 'A',
'objectB': 'B',
'objectC': 'C',
}
}";
var obj = StringToObject<ChildB>(json);
When you run the previous code, a ChildB object is created (because T type of StringToObject) and then, all the JSON string (public) properties are setted. So it's impossible that your type property may be useful to choose the type of the object. collection is not a property of your object: this is the reason why your object don't get these A,B,C values.
With this sample:
var childB = new ChildB
{
Id = "Id",
Message = "Msg",
TestObjectA = 1,
TestObjectB = 2,
TestObjectC = 3
};
var json = JsonConvert.SerializeObject(childB);
Your JSON must be like this:
{
"TestObjectA":1,
"TestObjectB":2,
"TestObjectC":3,
"Id":"Id",
"Message":"Msg"
}
And then, you get your valid object in this way:
var obj = StringToObject<ChildB>(json);
If you are going to work with distinct types and inheritance, then you must use the TypeNameHandling = TypeNameHandling.All that I commented before i my link https://stackoverflow.com/a/72270480/18452174.

Is there a way to define classes inline upon their usage?

I'm writing a service to retrieve data from an API over HTTP from another team in my company. The JSON response body from their API looks a bit like this:
"SomeObject": {
"SomeInnerObject": {
"SomeProperty": {
"Id": "123",
"Type": "abc",
"Name": "some value"
}
}
}
I'm writing a C# class to store the data in memory in order to do some comparisons. The nesting of the JSON object causes the class to look annoyingly repetitive:
public class MyClass
{
public SomeObjectModel SomeObject { get; set; }
public class SomeObjectModel
{
public SomeInnerObjectModel InnerObject { get; set; }
public class SomeInnerObjectModel
{
// etc...
}
}
}
I know for sure that the inner classes, like "SomeObjectModel", are only going to be read from and not instantiated elsewhere, so is there a way to combine the class definition and property definition lines into something more like this?
public class MyClass
{
public SomeObject { get; set; } :
{
public SomeInnerObject { get; set; } :
{
// etc...
}
}
}
EDIT:
The JSON will have arrays in it, so take that into account if you are proposing an alternative using generics, etc.
If you're using this just to deserialize the JSON, you don't even need to define a class.. You can use a nested anonymous type.
First, create and populate (with dummy values) an anonymous type that has the properties you need to be able to read. Then pass it as the second parameter to DeserializeAnonymousType<T>(string,T).
The result is a new instance of the same anonymous type that you created, but now it's populated with values from the JSON.
var json = #"{'SomeObject': {'SomeInnerObject': {'SomeProperty': {'Id': '123','Type': 'abc','Name': 'some value'}}}}";
var template = new
{
SomeObject = new
{
SomeInnerObject = new
{
SomeProperty = new
{
Id = default(int),
Type = default(string),
Name = default(string)
}
}
}
};
var result = JsonConvert.DeserializeAnonymousType(json, template);
var id = result.SomeObject.SomeInnerObject.SomeProperty.Id;
var type = result.SomeObject.SomeInnerObject.SomeProperty.Type;
var name = result.SomeObject.SomeInnerObject.SomeProperty.Name;
Console.WriteLine("{0} {1} {2}", id, type, name);
Output:
123 abc some value
See my working example on DotNetFiddle.
Edit: If your JSON contains an array, you can use new[] {} to create an array based on type inference and then put the anonymous types inside, like this:
var json = #"{ 'SomeObjects': [ { 'Id': '123', 'Name': 'some value' }, { 'Id': '456', 'Name': 'another value' } ]}";
var template = new
{
SomeObjects = new [] { new { Id=default(int), Name=default(string)} }
};
The short answer is no, C# does not support any version of that syntactic sugar. The closest thing in C# is probably either anonymous types or value tuples.
Other languages have similar features:
C++ supports "inline classes" in declarations.
Java supports anonymous classes that are more sophisticated than C# anonymous record types.
Scala supports case classes which declare their members in the header of the declaration.
And so on. I think the first is the closest thing to what you are looking for.
Scala-style class declarations have been proposed for C# many times in the last decade, and may finally make it into C# 8; see https://blog.cdemi.io/whats-coming-in-c-8-0-records/
You can use the dynamic type. Here is a minimal example:
using Newtonsoft.Json;
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
dynamic x = JsonConvert.DeserializeObject("[{key: '1001', value: 'test'}, {key: '1002', value: 'test2'}, ]");
Console.WriteLine(x[0].key);
Console.WriteLine(x[0].value);
Console.WriteLine(x[1].key);
Console.WriteLine(x[1].value);
Console.ReadLine();
}
}
}
You can create classes by using the paste special, paste JSON as classes.
https://channel9.msdn.com/Series/Windows-Store-Developer-Solutions/Quckly-Generate-C-Classes-from-JSON-Responses#time=01m56s

Newtonsoft json deserialise missing int values as nulls instead of zero

Is it possible to adjust JsonSerializerSettings that way?
(I guess not) but still have some hope, cause I'm not very experienced with their API.
By default and with all settings I've tried missing int either deserialized to 0 or causing exception.
Example
{
"defaultCaptionLineNum": 6,
"worksheets": {
"0": { "name": "Ws1", "caption_ln": 6 },
"1": { "name": "Ws2", "caption_ln": null },
"2": { "name": "Ws3" },
"3": { "name": "Ws4", "caption_ln": 5 },
}
}
Currently by default caption line (caption_ln) of Ws3 evaluated to 0, I want it to be evaluated to null (as in Ws2).
I'm using
jObject.ToObject<MyClass>(JsonSerializer.Create(settings));
to get object with worksheet(Ws) information from JSON (tried it without serializer as well)
and any variations of Include and Ignore here don't make jObject deserialize missing ints in json differently (with the exception of throwing error right away for missing values)
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
MissingMemberHandling = MissingMemberHandling.Ignore
};
Other serializer settings seem to be irrelevant.
UPD: Listing of MyObject MyClass.
public class MyClass
{
public class WorkSheet
{
[JsonProperty("name")]
string wsName;
[JsonProperty("caption_ln")]
int? captionLineNumber;
public WorkSheet(string name, int captionln)
{
wsName = name;
captionLineNumber = captionln;
}
}
private int _defaultCaptionLineNum;
[JsonProperty("defaultCaptionLineNum")]
public int DefaultCaptionLineNum { get => _defaultCaptionLineNum; }
private Dictionary<int, WorkSheet> _worksheets = new Dictionary<int, WorkSheet>();
[JsonProperty("worksheets")]
public Dictionary<int, WorkSheet> Worksheets { get => _worksheets; set => _worksheets = value; }
public MyClass()
{
Console.WriteLine("New MyClass object created!");
}
}
Your problem is that your type WorkSheet has only one constructor, which is parameterized:
public class WorkSheet
{
public WorkSheet(string name, int captionln)
{
wsName = name;
captionLineNumber = captionln;
}
// Remainder omitted
}
As explained in the documentation page ConstructorHandling Enumeration, Json.NET will fall back to a single parameterized constructor when there is no parameterless constructor, which is the case here. For the constructor arguments, Json.NET will match the JSON parameters to constructor arguments by name (modulo case), deserializing to the argument type if present or passing a default value if not present. And since there is no "captionln" parameter present in your JSON, captionLineNumber gets initialized to default(int), which is 0. This explains the problem you are seeing.
To fix the problem, you can change captionln to be of type int?:
public class WorkSheet
{
public WorkSheet(string name, int? captionln)
{
wsName = name;
captionLineNumber = captionln;
}
// Remainder omitted
}
Working fiddle #1 here.
Alternatively, you could introduce a private serialization-specific constructor and mark it with [JsonConstructor]:
public class WorkSheet
{
[JsonConstructor]
private WorkSheet() { }
public WorkSheet(string name, int captionln)
{
wsName = name;
captionLineNumber = captionln;
}
// Remainder omitted
}
Sample fiddle #2 here.
Related questions on constructor use in Json.NET:
How does JSON deserialization in C# work (whose answer has a cool diagram).
Json.net `JsonConstructor` constructor parameter names.
JSON.net: how to deserialize without using the default constructor?.

Pass an array in place of IList<string[]> in C#

In class below, I would like to sometimes pass just an array of strings for the property 'BodyParameters'.
Is that possible without using object type as its parameter type? Most times, this property will be a List of string [ ] arrays when the class is used.
public class EmailTemplate
{
...
public IList<string[]> BodyParameters { get; set; }
...
}
If you want to set BodyParameters using only a single string[], you can do this:
string[] value = ...;
myEmailTemplate.BodyParameters = new [] { value };
There is no implicit conversion from T to IList<T>, where T is string[] in your case.
The above code will work because new [] { ... } will infer the type string[][], which implements IList<string[]>.

What's the return type of an anonymous class

I have a class that used to have a string return type. Now I find I need to return more than a string. I was thinking to return something like below:
public string Test()
{
return ( new { ID = 5, Name= "Dave" } );
}
Is this even possible and if so then what would be the return type? I know it's not string ..
As others have said, the best thing to do here is to make a nominal type. I would suggest that the nominal type have the same characteristics as an anonymous type; that is, you should consider making the type immutable and consider making it exhibit value equality.
It is possible to return an anonymous type as object and then use the instance returned elsewhere using a variety of sneaky techniques. You can cast the object to "dynamic" (in C# 4) and then use the properties of the anonymous type, but this is slow and lacks compile-time type checking.
You can also use the "cast by example" trick, which does get you compile-time type checking. However, that trick only works when the anonymous source object and the anonymous example object come from the same assembly.
static T CastByExample<T>(object source, T example) where T : class
{
return source as T;
}
static object ReturnsAnonymous() { return new { X = 123 }; }
static void DoIt()
{
object obj = ReturnsAnonymous();
var example = new { X = 0 };
var anon = CastByExample(obj, example);
Console.WriteLine(anon.X); // 123
}
See how sneaky that is? We use method type inference and local variable type inference to tell the compiler "these two things are the same type". This lets you export an anonymous type as object and cast it back to anonymous type.
But you probably should not do this; if you're resorting to such sneaky tricks then you should simply be defining a nominal type in the first place. Also, like I said, the trick only works if the example and the source objects were created in code in the same assembly; two "identical" anonymous types in two different assemblies do not unify to be the same type.
The object that you return does have a class, but it's anonymous so you can't specify it in the code. You just have to return it as an object reference:
public object Test() {
return new { ID = 5, Name= "Dave" };
}
Note that the anonymous type is unknown outside the scope of the method, so reflection is the only way to access its properties.
If you want to be able to use the returned object conveniently, you should declare a class:
public class TestResult
{
public int ID { get; set; }
public string Name { get; set; }
}
public TestResult Test() {
return new TestResult() { ID = 5, Name= "Dave" };
}
Another alternative is to use an existing class, if it fits your purpose. A KeyValuePair is close to what you use, but then the properties will of course be named Key and Value instead of ID and Name:
public KeyValuePair<int, string> Test() {
return new KeyValuePair<int, string>(5, "Dave");
}
This isn't possible as the anonymous class is only valid within the current context. If you need to return an object then you'll need to create a real class.
I'm assuming you left string as the return type by accident.
Anonymous type are class type that are derived directly from object.
You can return it from method as object as return type.
Have a look at this.
No, it's not possible. Your options are:
Define a real class for the return value,
Use System.Tuple, or
Use out parameters (probably the least good option).
You can make a struct (or class) for this.
public struct IdAndName
{
public int Id;
public string Name;
public IdAndName(int id, string name)
{
ID = id;
Name = name;
}
}
You could also use a Tuple<T1, T2>, (but that's not recommended as the properties aren't named.
class NewString
{
public int ID { get; set; }
public string Name { get; set; }
}
public NewString Test()
{
return ( new NewString() { ID = 5, Name = "Dave" } );
}
:)

Categories