Create a fixture for recursive data structure with AutoFixture - c#

I'm working on a project where I have some recursive data structure and I want to create a fixture for it.
The data structure is XmlCommandElement, it has a single method ToCommand that converts XmlCommandElement to Command.
Each node on the tree can be a XmlCommandElement and/or XmlCommandPropertyElement.
Now, in order to test the behaviour of the method ToCommand I want to fetch XmlCommandElement with some arbitrary data.
I want to control the depth of the tree and the amount of instances of XmlCommandElement and/or XmlCommandPropertyElement per node.
So here is the code I'm using for the fixture:
public class XmlCommandElementFixture : ICustomization
{
private static readonly Fixture _fixture = new Fixture();
private XmlCommandElement _xmlCommandElement;
public int MaxCommandsPerDepth { get; set; }
public int MaxDepth { get; set; }
public int MaxPropertiesPerCommand { get; set; }
public XmlCommandElementFixture BuildCommandTree()
{
_xmlCommandElement = new XmlCommandElement();
var tree = new Stack<XmlCommandElementNode>();
tree.Push(new XmlCommandElementNode(0, _xmlCommandElement));
while (tree.Count > 0) {
var node = tree.Pop();
node.Command.Key = CreateRandomString();
node.Command.Properties = CreateProperties();
if (MaxDepth > node.Depth) {
var commands = new List<XmlCommandElement>();
for (var i = 0; i < MaxCommandsPerDepth; i++) {
var command = new XmlCommandElement();
tree.Push(new XmlCommandElementNode(node.Depth + 1, command));
commands.Add(command);
}
node.Command.Commands = commands.ToArray();
}
}
return this;
}
public void Customize(IFixture fixture)
{
fixture.Customize<XmlCommandElement>(c => c.FromFactory(() => _xmlCommandElement)
.OmitAutoProperties());
}
private static string CreateRandomString()
{
return _fixture.Create<Generator<string>>().First();
}
private XmlCommandPropertyElement[] CreateProperties()
{
var properties = new List<XmlCommandPropertyElement>();
for (var i = 0; i < MaxPropertiesPerCommand; i++) {
properties.Add(new XmlCommandPropertyElement {
Key = CreateRandomString(),
Value = CreateRandomString()
});
}
return properties.ToArray();
}
private struct XmlCommandElementNode
{
public XmlCommandElementNode(int depth, XmlCommandElement xmlCommandElement)
{
Depth = depth;
Command = xmlCommandElement;
}
public XmlCommandElement Command { get; }
public int Depth { get; }
}
}
And this is how I'm using it:
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
This works perfectly fine! but the issue I have with it is it isn't generic, the whole point of AutoFixture at least as far as I know is to avoid making specific fixtures.
So what I would really like to do is something like this (found it here but it doesn't work for me.):
var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthThrowingRecursionBehavior(2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandElement), 3));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandPropertyElement), 4));
xmlCommandElement = fixture.Create<XmlCommandElement>();
Here is all the code for reference:
Interfaces:
public interface ICommandCollection : IEnumerable<ICommand>
{
ICommand this[string commandName] { get; }
void Add(ICommand command);
}
public interface ICommandPropertyCollection : IEnumerable<ICommandProperty>
{
string this[string key] { get; }
void Add(ICommandProperty property);
}
public interface ICommandProperty
{
string Key { get; }
string Value { get; }
}
public interface ICommand
{
ICommandCollection Children { get; set; }
string Key { get; }
ICommandPropertyCollection Properties { get; }
}
public interface ICommandConvertible
{
ICommand ToCommand();
}
Classes:
public sealed class CommandPropertyCollection : ICommandPropertyCollection
{
private readonly IDictionary<string, ICommandProperty> _properties;
public CommandPropertyCollection()
{
_properties = new ConcurrentDictionary<string, ICommandProperty>();
}
public string this[string key]
{
get
{
ICommandProperty property = null;
_properties.TryGetValue(key, out property);
return property.Value;
}
}
public void Add(ICommandProperty property)
{
_properties.Add(property.Key, property);
}
public IEnumerator<ICommandProperty> GetEnumerator()
{
return _properties.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public sealed class CommandProperty : ICommandProperty
{
public CommandProperty(string key, string value)
{
Key = key;
Value = value;
}
public string Key { get; }
public string Value { get; }
}
public sealed class Command : ICommand
{
public Command(string key, ICommandPropertyCollection properties)
{
Key = key;
Properties = properties;
}
public ICommandCollection Children { get; set; }
public string Key { get; }
public ICommandPropertyCollection Properties { get; }
}
public class XmlCommandPropertyElement : ICommandPropertyConvertible
{
[XmlAttribute("key")]
public string Key { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
public ICommandProperty ToCommandProperty()
{
return new CommandProperty(Key, Value);
}
}
Finally, the class I'm trying to test is as follow:
public class XmlCommandElement : ICommandConvertible
{
[XmlArray]
[XmlArrayItem("Command", typeof(XmlCommandElement))]
public XmlCommandElement[] Commands { get; set; }
[XmlAttribute("key")]
public string Key { get; set; }
[XmlArray]
[XmlArrayItem("Property", typeof(XmlCommandPropertyElement))]
public XmlCommandPropertyElement[] Properties { get; set; }
public ICommand ToCommand()
{
ICommandPropertyCollection properties = new CommandPropertyCollection();
foreach (var property in Properties) {
properties.Add(property.ToCommandProperty());
}
ICommand command = new Command(Key, properties);
return command;
}
}
The test itself looks like this:
namespace Yalla.Tests.Commands
{
using Fixtures;
using FluentAssertions;
using Ploeh.AutoFixture;
using Xbehave;
using Yalla.Commands;
using Yalla.Commands.Xml;
public class XmlCommandElementTests
{
[Scenario]
public void ConvertToCommand(XmlCommandElement xmlCommandElement, ICommand command)
{
$"Given an {nameof(XmlCommandElement)}"
.x(() =>
{
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
});
$"When the object is converted into {nameof(ICommand)}"
.x(() => command = xmlCommandElement.ToCommand());
"Then we need to have a root object with a key"
.x(() => command.Key.Should().NotBeNullOrEmpty());
"And 4 properties as its children"
.x(() => command.Properties.Should().HaveCount(4));
}
}
}
Thanks to Mark Seemann! the final solution looks like this:
public class RecursiveCustomization : ICustomization
{
public int MaxDepth { get; set; }
public int MaxElements { get; set; }
public void Customize(IFixture fixture)
{
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(MaxDepth));
fixture.RepeatCount = MaxElements;
}
}
And can be used like this:
xmlCommandElement = new Fixture().Customize(new RecursiveCustomization {
MaxDepth = 2,
MaxElements = 3
}).Create<XmlCommandElement>();

You can fairly easily create a small tree by changing the Fixture's recursion behaviour:
[Fact]
public void CreateSmallTree()
{
var fixture = new Fixture();
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(recursionDepth: 2));
var xce = fixture.Create<XmlCommandElement>();
Assert.NotEmpty(xce.Commands);
}
The above test passes.

Related

Adding custom logics to Mapster's mapping, possibly with AfterMapping?

After mapping Input to Output, the returned Input must have an incrementing number in its Entries[i].Id, (i.e. Entries[0].Id = 1, Entries[1].Id = 2, Entries[2].Id = 3, ...). So I tried to put it in AfterMapping:
[Mapper]
public interface IMyMapper {
Output Map(Input userTableTemplate);
}
public class RegisterMyMapper : IRegister {
public void Register(TypeAdapterConfig config) {
config.NewConfig<Input, Output>()
.Map(output => output.OutputName, input => input.Name)
.AfterMapping(output => {
foreach (var pair in output.Entries.Select((value, index) => new {value = value, index = index})) {
pair.value.Id = pair.index + 1;
}
});
}
}
public class Output {
public string OutputName { get; set; }
public IEnumerable<Entry> Entries { get; set; }
}
public class Entry { public int Id { get; set; } }
public class Input { public string Name { get; set; } }
But when running _myMapper.Map(myInput) I'm getting a null reference exception, because the private Action<Output> Action1 member of the generated code (public partial class MyMapper : IIMyMapper) is null:
public partial class MyMapper : IMyMapper
{
private Action<Output> Action1;
public Output Map(Input p1)
{
if (p1 == null)
{
return null;
}
Output result = new Output();
result.OutputName = p1.Name;
Action1.Invoke(result);
return result;
}
}

Unable to determine the serialization information when create index

I'm having problem when i create a index.
This is my code:
private static IMongoCollection<IPageItem> GetCollection()
{
return Connection.Database.GetCollection<IPageItem>("SystemPages");
}
internal static IEnumerable<Task> CreateIndex()
{
BsonClassMap.RegisterClassMap<NewsItem>();
var work = new List<Task>();
var coll = GetCollection();
var builder= Builders<IPageItem>.IndexKeys;
var btIndexes = new List<CreateIndexModel<IPageItem>>
{
new(builder.Ascending(x => x.PageId), new CreateIndexOptions { Unique = true })
};
work.Add(coll.Indexes.CreateManyAsync(btIndexes));
return work;
}
public interface IPageItem
{
public Guid PageId { get; set; }
}
public class NewsItem : IPageItem
{
public Guid PageId { get; set; }
public string Summery { get; set; }
}
When i call, CreateIndex() i receive, Unable to determine the serialization information for x => Convert(x.PageId, Object).)
Can I not use one interface when create/get a collection?

Mapping the external api request based on a pattern from a general request

I have an application with a general search request which has an identification number that could be anything like product number or customer number along with additional search criteria. I want to display all the results. I am writing a middleware to call the search api end points.
public class GeneralRequest
{
public string IdentificationNumber { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
public class AdditionalSearch
{
public RangeSearch Range { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
}
public class RangeSearch
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
public class GetProductRequest : AdditionalSearch, ISearchRequest
{
public string ProductId { get; set; }
}
public class GetCustomerRequst : AdditionalSearch, ISearchRequest
{
public string CustomerNumber { get; set; }
}
public class GetManufacturerRequest : AdditionalSearch, ISearchRequest
{
public string ManufacturerNumber { get; set; }
}
// this is a dummy interface to make all the requests general
public interface ISearchRequest
{
}
This is the searchprocessor where I am creating the correct request based on the identification number pattern. But I am failing to assign the AdditonalSearch to the request that I get after invoking the func. Of course it is an interface that has nothing in it. How can I achieve this by not repeating(I mean I don't want to repeat the initialization logic in the dictionary)
Please suggest me what is the best practice here.
public class SearchProcessor
{
private readonly Dictionary<Regex, Func<GeneralRequest, ISearchRequest>> _pattern;
private readonly IAppClient _appClient;
public SearchProcessor(IAppClient appClient)
{
_appClient = appClient;
_pattern = new Dictionary<Regex, Func<GeneralRequest, ISearchRequest>>
{
{new Regex("/\b([0-9]|10)\b /"), p=> new GetProductRequest(){ProductId = p.IdentificationNumber} },
{new Regex("^\\d{}1,9}$"), p=> new GetCustomerRequst(){CustomerNumber = p.IdentificationNumber} },
{new Regex("^\\d{}1,11}$"), p=> new GetManufacturerRequest(){ManufacturerNumber = p.IdentificationNumber} }
};
}
public List<SearchResult> GetAllSearchResults(GeneralRequest request)
{
var requests = _pattern.Where(r => r.Key.IsMatch(request.IdentificationNumber)).Select(v => v.Value);
var responses = new List<SearchResult>();
foreach (var req in requests)
{
var appRequest = req.Invoke(request);
appRequest.AdditionalSearch = new AdditionalSearch // this is where I am not able to assign the additional seach from the general request
{
Range = new RangeSearch { Start = request.Start, Stop = request.Stop}
IsActive = request.IsActive,
Location = request.Location
};
//This calls another api to get the response.
responses.Add(_appclient.FindResult(appRequest));
}
return responses;
}
}
---UPDATE--
Here is the appclient that calls the external api..
sample request for the getproduct route is
public class AppClient : IAppClient
{
private readonly string _baseurl;
private readonly string _getProductRoute;
private readonly string _getCustomerRoute;
private readonly string _getManufacturerRoute;
public AppClient()
{
_getProductRoute = $"{_baseurl}/api/get-product";
_getCustomerRoute = $"{_baseurl}/api/get-customer";
_getManufacturerRoute = $"{_baseurl}/api/get-Manufacturer";
}
public SearchResult FindResult(ISearchRequest searchRequest)
{
var routes = new Dictionary<Type, string>
{
{typeof(GetProductRequest), _getProductRoute },
{typeof(GetCustomerRequst), _getCustomerRoute },
{typeof(GetManufacturerRequest), _getManufacturerRoute }
};
// Here it is going to be http implementation to call above routes.
return new SearchResult();
}
}
The request for get product route is
{
"ProductId":"",
"RangeSearch":{
"Start":"",
"Stop":""
},
"Location":"",
"IsActive":true
}
For get-customer request is
{
"CustomerNumber":"",
"RangeSearch":{
"Start":"",
"Stop":""
},
"Location":"",
"IsActive":true
}
You can simplify your work by doing this :
public class SearchRequest
{
public string IdentificationNumber { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
public SearchRequestRange Range { get; set; }
public SearchRequest(string identificationNumber)
{
IdentificationNumber = identificationNumber;
}
}
public class SearchRequestRange
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
now the process you are doing is not needed, you only need to adjust your ApiClient to something like this :
public class AppClient : IAppClient
{
private static readonly IReadOnlyDictionary<string, string> _endPointsSearchPatterns = new Dictionary<string, string>
{
{"get-product", "/\b([0-9]|10)\b /"},
{"get-customer", "^\\d{}1,9}$"},
{"get-Manufacturer", "^\\d{}1,11}$"}
};
private readonly string _baseUrl;
public AppClient(string baseUrl)
{
if(string.IsNotNullOrWhiteSpace(baseUrl))
throw new ArgumentNullException(nameof(baseUrl));
_baseurl = baseUrl;
}
public IEnumerable<SearchResult> FindResult(SearchRequest searchRequest)
{
var endPoints = _endPointsSearchPatterns.Where(x=> Regex.IsMatch(request.IdentificationNumber , x.Value))?.ToList();
if(endPoints?.Count == 0)
{
yield break;
}
var responses = new List<SearchResult>();
foreach(var endpoint in endPoints)
{
ISearchBy search = null;
switch(endPoint.Key)
{
case "get-product":
action = new ProductApiClient(this);
break;
case "get-customer":
action = new ProductApiClient(this);
break;
case "get-Manufacturer":
action = new ProductApiClient(this);
break;
}
yield return action.SearchBy(searchRequest);
}
return searchResult;
}
}
Regarding GetProductRequest, GetCustomerRequst, and GetManufacturerRequest these should be refactored, and instead you can create a class for each entity like this :
public interface ISearchBy
{
SearchResult ISearchBy(SearchRequest request);
}
public class ProductApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public ProductApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
public class CustomerApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public CustomerApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
public class ManufacturerApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public ManufacturerApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}

Get the typed object with pattern factory

see my code :
public interface IStructureType
{
int Longueur { get; set; }
int Position { get; set; }
int CompleterCodeBy { get; set; }
}
public abstract class StructureTypeFactory
{
public abstract IStructureType GetStructureType(string type);
}
public class ConcreteStructureTypeFactory : StructureTypeFactory
{
public override IStructureType GetStructureType(string type)
{
switch(type)
{
case "StructureCodeMagasin":
return new StructureCodeMagasin();
case "StructureChrono":
return new StructureChrono();
case "StructureLotSimple":
return new StructureLotSimple();
default:
throw new ApplicationException("");
}
}
}
public class StructureCodeMagasin : IStructureType
{
public int Longueur { get ; set; }
public int Position { get; set; }
public int CompleterCodeBy { get { return 2; } set { CompleterCodeBy = value; } }
public void GetCodeMagasin()
{
//some code
}
}
I try to use Factory pattern, but how I can access to method GetCodeMagasin in this example :
public MainWindow()
{
InitializeComponent();
StructureTypeFactory st = new ConcreteStructureTypeFactory();
var structure = st.GetStructureType("StructureCodeMagasin");
int longueur = structure.CompleterCodeBy;
}
I can access properties but no method, I would like structure variable will typed StructureCodeMagasin.
Thanks for help

Dynamic class based on string parameter

I have this:
public class Blah
{
public int id { get; set; }
public string blahh { get; set; }
}
public class Doh
{
public int id { get; set; }
public string dohh { get; set; }
public string mahh { get; set; }
}
public List<???prpClass???> Whatever(string prpClass)
where string prpClass can be "Blah" or "Doh".
I would like the List type to be class Blah or Doh based on what the string prpClass holds.
How can I achieve this?
EDIT:
public List<prpClass??> Whatever(string prpClass)
{
using (var ctx = new ApplicationDbContext())
{
if (prpClass == "Blah")
{
string queryBlah = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Blah>(queryBlah).ToList();
return result;
}
if (prpClass == "Doh")
{
string queryDoh = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Doh>(queryDoh).ToList();
return result;
}
return null
}
}
you have to have a common supertype:
public interface IHaveAnId
{
int id { get;set; }
}
public class Blah : IHaveAnId
{
public int id { get; set; }
public string blahh { get; set; }
}
public class Doh : IHaveAnId
{
public int id {get;set;}
public string dohh { get; set; }
public string mahh { get; set; }
}
then you can do:
public List<IHaveAnId> TheList = new List<IHaveAnId>();
and in some method:
TheList.Add(new Blah{id=1,blahh = "someValue"});
TheList.Add(new Doh{id =2, dohh = "someValue", mahh = "someotherValue"});
to iterate through the list:
foreach(IHaveAnId item in TheList)
{
Console.WriteLine("TheList contains an item with id {0}", item.id);
//item.id is allowed since you access the property of the class over the interface
}
or to iterate through all Blahs:
foreach(Blah item in TheList.OfType<Blah>())
{
Console.WriteLine("TheList contains a Blah with id {0} and blahh ='{1}'", item.id, item.blahh);
}
Edit:
the 2 methods and a int field holding the autovalue:
private int autoValue = 0;
public void AddBlah(string blahh)
{
TheList.Add(new Blah{id = autovalue++, blahh = blahh});
}
public void AddDoh(string dohh, string mahh)
{
TheList.Add(new Doh{id = autovalue++, dohh = dohh, mahh = mahh});
}
Another Edit
public List<object> Whatever(string prpClass)
{
using (var ctx = new ApplicationDbContext())
{
if (prpClass == "Blah")
{
string queryBlah = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Blah>(queryBlah).ToList();
return result.Cast<object>().ToList();
}
if (prpClass == "Doh")
{
string queryDoh = #"SELECT ... ";
var result = ctx.Database.SqlQuery<Doh>(queryDoh).ToList();
return result.Cast<object>.ToList();
}
return null;
}
}
in the view you then have to decide what type it is. In asp.net MVC you can use a display template and use reflection to get a good design. But then i still don't know what technology you are using.
Yet another Edit
TestClass:
public class SomeClass
{
public string Property { get; set; }
}
Repository:
public static class Repository
{
public static List<object> Whatever(string prpClass)
{
switch (prpClass)
{
case "SomeClass":
return new List<SomeClass>()
{
new SomeClass{Property = "somestring"},
new SomeClass{Property = "someOtherString"}
}.Cast<object>().ToList();
default:
return null;
}
}
}
And a controller action in mvc:
public JsonResult Test(string className)
{
return Json(Repository.Whatever("SomeClass"),JsonRequestBehavior.AllowGet);
}
then i called it with: http://localhost:56619/Home/Test?className=SomeClass
And got the result:
[{"Property":"somestring"},{"Property":"someOtherString"}]
Is this what you are trying to do?
public class Blah
{
public int id { get; set; }
public string blahh { get; set; }
}
public class Doh
{
public int id { get; set; }
public string dohh { get; set; }
public string mahh { get; set; }
}
class Program
{
public static List<T> Whatever<T>(int count) where T: new()
{
return Enumerable.Range(0, count).Select((i) => new T()).ToList();
}
static void Main(string[] args)
{
var list=Whatever<Doh>(100);
// list containts 100 of "Doh"
}
}

Categories

Resources