I'm using SpecFlow for the very first time to write tests for my project and I ran into a small problem.
I have the next class:
public class FancyName
{
[DataMember]
public Guid Guid { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List <Country> Countries { get; set; }
}
And I want to generate this class in my Tests using SpecFlow helpers.
Here is the part of Scenario:
[...]
When i add some names
| Name | Countries |
| UK | 1 |
| US | 2 |
[...]
I try to parse it in step definitions like this:
[When(#"I add some names")]
public void AddNames(Table table)
{
var names = table.CreateSet<FancyName>();
[...]
}
And I'm running into 2 problems:
I do not pass the Guid because a want to generate it like Guid.NewGuid() so created object contain null
I pass countries as sorting but i need to create List<Country>().
I used to try iterate through Table and create FancyName objects manually but as I understand it is not SpecFlow way. I tried to look through documentation and wasn't lucky to find proper solution.
May be somebody know the really good way to solve this?
Thanks in advance.
The main SpecFlow.Assist author here...
I agree with #sam-holder above that Table.CreateSet<T> is not magic, and perhaps the transform is an easier solution in this case. But Assist does, in deed, have the features needed to do what is asked. :)
I'd like to explain how. I see two issues presented as:
1) How to set the Guid on each record?
2) How to convert the string value to an array?
For (1), the answer is pretty simple. You can pass a function to CreateSet that tells the library how to instantiate the object you want to create. Since you just want to set the Guid, you can do it like so:
table.CreateSet<FancyName>(() => new FancyName { Guid = Guid.NewGuid()});
// or simpler, table.CreateSet(() => new FancyName { Guid = Guid.NewGuid()});
For (2), you'll have to do a little more programming. You want Assist to know how to convert the string to a list. To do this, you'll have to create a "value retriever" and register it with Assist. I was able to do this with the following code:
public class CountryRetriever : IValueRetriever
{
public bool CanRetrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return propertyType.FullName.StartsWith("System.Collections.Generic.List`1[[SpecFlowExample.Country");
}
public object Retrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return keyValuePair.Value.Split(',')
.Select(x => new Country {Name = x})
.ToList();
}
}
[Binding]
public class Steps
{
[BeforeTestRun]
public static void Setup()
{
TechTalk.SpecFlow.Assist.Service.Instance.RegisterValueRetriever(new CountryRetriever());
}
[When(#"i add some names")]
public void WhenIAddSomeNames(Table table)
{
var things = table.CreateSet<FancyName>(() => new FancyName { Guid = Guid.NewGuid()});
}
}
Pointing directly at a list like this, especially using a string, is pretty hacky, but this code does work.
The country retriever will announce that it can handle the transformation of a string when it is confronted with a List<Country> type. It then splits the string and creates a list of countries.
SpecFlow starts each test run with value retrievers for most of the base .Net types, but not your List<Country>. To accomodate your type, you'll need to register your new value retriever before every test run.
Table.CreateSet<> can't perform magic. It can't know that its supposed to create a new Guid for your object, or that its supposed to create a list containing 2 countries. You'll have to create this object yourself I think.
The best way to solve this is to use a [StepArgumentTransformation]
something like this:
[StepArgumentTransformation]
public List<FancyName> TransformToFancyName(Table table)
{
//create the list from the table contents
}
[When(#"I add some names")]
public void AddNames(List<FancyName> names)
{
.. use your FancyNames here
}
specflow will call your StepArgumentTransformation for any Step which has an argument of List<FancyName> as the last parameter and a corresponding table in the feature
You can think about something like Nested Tables?, but according to this post it's a bad idea. It suggests to introduce additional step to populate complex objects.
Related
I'm writing a wrapper around certain functions of mongodb to enforce certain buisiness policies (such as having a last modified date, a document version &c). These extra fields will not appear in the model and will be irrelevant and transparent to the person implementing against this library. This library will be generic.
Therefore using replaceOne is out of the question.
What I would like is some way of passing all fields in a person passed object to the Update builder - so I can use .Set/.Inc accordingly to add the other fields.
An example to demonstrate what I want is below:
public static async Task UpdatePerson(string name, Person person)
{
var client = new MongoClient("mongodb://localhost:27017");
IMongoDatabase db = client.GetDatabase("test");
IMongoCollection<Person> collection = db.GetCollection<Person>("people");
var query = Builders<Person>.Filter
.Eq("name", name);
var update = Builders<Person>.Update
//Something here - how do I pass my person's properties?
.Set("lastModified", DateTime.Now)
.Inc("version",1);
await collection.UpdateOneAsync(query, update );
}
//--
//In real life this'll work for other types, this is for demonstration only
public class Person
{
public string name {get;set;}
public string surname {get;set;}
}
So how can I go about this, without, for instance, looping through properties using Reflection?
Not sure if you are able to do this but the Mongodb Driver provides something called [BsonExtraElements].
public class Person
{
public string name {get;set;}
public string surname {get;set;}
[BsonExtraElements]
public Dictionary<string,object> AdditionalFields { get; set; }
}
What will happen is that anything that cant be serialized to the model will be filled into that dictionary, no matter the type. You can add to it as well and remove.
This will add no additional overhead to your database, The only downside to this is that querying this dictionary is somewhat not a great experience as you may need to cast specific keys to their relevant expected types.
If this is not viable I suggest the BSON approach recommended by Simon.
First of all, apologies if I posted it in the wrong place, I'm new here and I'm not sure if I posted in the right place.
Well, I'm trying to build a generic search method, where I'll add search parameters to mount a SQL Query and execute it on the database. All that using C#. My goal is that the parameter corresponding to the field I'll search, to be a property of the class the method is in. For example:
public foo
{
public string CustomerCode { get; set; }
public string CustomerName { get; set; }
public void AddSearchParameter(???, EnumOperator Operator, object Value)
}
Whenever I want to specify a parameter to add on the search, I would like it to look like this:
foo xxx = new foo();
xxx.AddSearchParameter(foo.CustomerCode, EnumOperator.Equal, txtCustomerCode.text);
My question is how to do it?
If you are trying to pass the member information (so that the AddSearchParameter can inspect the MemberInfo and write suitable SQL), then you'd need to use either a string literal (i.e. "CustomerCode"), or an expression tree. The latter is richer, but involves learning the Expression<T> API. But fundamentally:
public void AddSearchParameter(Expression<Func<object>> expression, ...)
...
xxx.AddSearchParameter(() => foo.CustomerCode, ...)
This, however, is not a trivial area of .NET.
If I were doing something like this, I would probably make the Search() method on foo check for the existence of values in the various this properties, and then build the query based on that.
public List<Results> Search()
{
if (!String.IsNullOrEmpty(this.CustomerCode))
{
// add search value to query
}
// etc.
}
Thank you for all your messages that really helped me, I finally decide to post here. I just discovered Tuples which allow me to create List of List with other element.
Here is my simplified code :
public class Category { //Classe "Category" comprenant le nom de la category et sa proportion de CIR
public string category_name {get; set;}
public string proportion {get; set;}
}
public static void Main(string[] args)
{
var Projects = new List<Tuple<string, List<Category>>>();
while(...)
{ [...]
var List_Categories = new List<Category>()
while(...)
{ [...]
List_Categories.Add(category_example);
}
Projects.Add(Tuple.Create(nom_projet, List_Categories);
}
}
My "Category" class is just 2 strings. A "project" is a List AND a string (project_name) hence my using of Tuple for this.
1st question : I would have create a class for "project" but I could not find how to put a Tuple as a parameter of a class ? (he do not recognize the "var" type as a parameter)
Then, 2nd question : I have an issue : when the program going on, inside "Projects" (which is a List of "project"), the "project_name" is writting well but the "Category" data is each time replaced by the new one for EVERY index of the list. I do not know how such a thing is possible ...
Thank you very much, I hope you can understand my problem, I am very sorry about my English but it is not my native language. I will reformulate if it's not understandable.
It doesn't seem like you want a tuple as a property in your project class, it seems like what you want is this:
public class Project
{
public string project_name {get; set;}
public List<Category> categories {get; set;}
}
And as Corak mentioned, I think a Dictionary might help with your other issue.
By the way, the reason you can't use var as the type of a property is that it isn't a type -- it's just a shorthand way of declaring a variable without explicitly specifying the type of that variable (the compiler figures it out), but it only works on local variables. You can't use it for a property because the compiler would have no way to figure out what the type of that property is supposed to be. The type of a tuple is Tuple<T1, T2,...> -- for example, your tuples here are Tuple<string, List<Category>>, and you could certainly have a class property of that type if you wanted.
Try using a dictionary instead
public static void Main(string[] args)
{
var Projects = new Dictionary<string, List<Category>>();
while(...)
{ [...]
var List_Categories = new List<Category>()
while(...)
{ [...]
List_Categories.Add(category_example);
}
Projects.Add(nom_projet, List_Categories);
}
}
By creating a tuple on every single loop, you are overwriting previous work. With a dictionary you can simply add to your collection inside of your loops
I have a class in one application - that I cannot change (legacy) - that is inside of a assembly (DLL file):
public class ShippingMethod
{
public string ShipMethodCode { get; set; }
public string ShipMethodName { get; set; }
public decimal ShippingCost { get; set; }
public List<ShippingMethod> GetAllShippingMethods()
{
......
}
}
I have a second application that is referencing that assembly (DLL file) and needs to populate a drop-down with all the Shipping Methods. Ex: "UPS - $3.25"
The issue is that it needs to be using the correct format for different currencies. Ex: $3.25 or 3.25€ depending on a parameter called CountryID.
I have written a function String DisplayMoney(Decimal Amount, Integer CountryID) that will return the correct format of the amount.
Now I need to apply this function to every shipping method and save it into a new list.
What is the best way to do this?
I can create another class called LocalizedShippingMethods as follows:
public class LocalizedShippingMethod
{
public ShippingMethod ShipMethod { get; set; }
public string LocalizedShippingCost { get; set; }
}
Is this the best way to accomplish this? Is there a better way to do this using inheritance? And if I use inheritance, how do I get the values from the first LIST into the NEW LIST?
That is indeed a good method of doing it. You can use a pretty quick Linq query to pull the old List into the new one:
List<LocalizedShippingMethod> Translate(List<ShippingMethod> oldList)
{
return oldList.Select(a => new LocalizedShippingMethod
{
// Initialize properties according to however you translate them
}).ToList();
}
Additionally, to make this more streamlined and obvious, you could do any of the following to aid in the translation:
Create a constructor for LocalizedShippingMethod that takes in a ShippingMethod and properly sets the properties
Create a static method on LocalizedShippingMethod that takes in a ShippingMethod and returns an initialized LocalizedShippingMethod
Create an operator on LocalizedShippingMethod that converts from a ShippingMethod
Create an extension method on ShippingMethod, call it ToLocalized() that returns a LocalizedShippingMethod
What if you create an extension method for the ShippingMethod class?
The best way to do this is whatever way works best for you. If you're the person who's going to have to maintain this code, what will make your life the easiest down the road?
Once you've answered that question, that is the best solution.
Let's say I have the following classes.
public class MyClass {
public string Data1 { get; set; }
public MyOtherClass Data2 { get; set; }
// 50+ other properties...
}
public class MyOtherClass {
public string OtherData1 { get; set; }
// More properties
}
There's somecode that instanciate that class and populates it with all the data. I'd like to use that object for my test. I could simply serialize the structure into XML and reload it later. However, what I would really like is to have the entire object tree build in the code. In other words:
MyClass myClass = new MyClass {
Data1 = "Hello",
Data2 = new MyOtherClass {
OtherData1 = "World",
// More...
},
// More...
}
I could write all that myself, but it would take hours and be error prone since there's a high number of properties and sub-classes. Here's my question: given an object how would you generate the code which populate that object?
I would write a T4 template. Check out an example that is doing something, although really remotely, similar to what you need.
I would use json for a data format and use something like http://json2csharp.com to generate classes to use to serialize and deserialize to and from json. Or given the classes already existing annotate them and serialize them out.
This will handle any arbitrary nesting and be maintainable. Values can even be edited without a recompile which is usually a good thing. The link also leads to examples for how to specify specific types, handle enums, object links, etc.
Perhaps if you specify why it absolutely has to be generated from code only we can give better answers.