I have a class that inherits from the abstract Configuration class, and then each class implements the reader for INI files, XML, conf, or proprietary formats. I am having a problem in creating the objects to be tested using FakeItEasy.
The object I am trying to test uses the configuration object via Dependency Injection, so it can simply read configuration settings by calling the ReadString(), ReadInteger() etc... functions, and then the text for the location (Section, Key for instance, with an INI) may be retrieved from the appropriate section in whatever format of configuration file (INI, XML, conf, etc...).
Sample code being used:
public class TestFile
{
private readonly ConfigurationSettings ConfigurationController_ ;
...
public TestFile(ConfigurationSettings ConfigObject)
{
this.ConfigurationController_ = ConfigObject;
}
public TestFile(XMLFile ConfigObject)
{
this.ConfigurationController_ = ConfigObject;
}
public TestFile(INIFile ConfigObject)
{
this.ConfigurationController_ = ConfigObject;
}
...
private List<string> GetLoginSequence()
{
List<string> ReturnText = new List<string>();
string SignOnFirst = ConfigurationController_.ReadString("SignOn", "SignOnKeyFirst", "Y");
string SendEnterClear = ConfigurationController_.ReadString("SignOn", "SendEnterClear", "N");
if (SendEnterClear.Equals("Y", StringComparison.CurrentCultureIgnoreCase))
{
ReturnText.Add("enter");
ReturnText.Add("clear");
}
if (SignOnFirst.Equals("N", StringComparison.CurrentCultureIgnoreCase))
{
ReturnText.AddRange(MacroUserPassword("[$PASS]"));
ReturnText.Add("sign_on");
}
else
{
ReturnText.Add("sign_on");
ReturnText.AddRange(MacroUserId("[$USER]"));
ReturnText.AddRange(MacroUserPassword("[$PASS]"));
}
return ReturnText;
}
A Simple Test example:
[TestMethod]
public void TestSignOnSequence()
IniReader FakeINI = A.Fake<IniReader>();
//Sample Reads:
//Config.ReadString("SignOn", "SignOnKeyFirst", "Y");
//Config.ReadString("SignOn", "SendEnterClear", "N"); // Section, Key, Default
A.CallTo(() => FakeINI.ReadString(A<string>.That.Matches(s => s == "SignOn"), A<string>.That.Matches(s => s == "SendEnterClear"))).Returns("N");
A.CallTo(() => FakeINI.ReadString(A<string>.That.Matches(s => s == "SignOn"), A<string>.That.Matches(s => s == "SignOnKeyFirst"))).Returns("N");
A.CallTo(FakeINI).Where( x => x.Method.Name == "ReadInteger").WithReturnType<int>().Returns(1000);
TestFile TestFileObject = new TestFile(FakeINI);
List<string> ReturnedKeys = TestFileObject.GetLoginSequence();
Assert.AreEqual(2, ReturnedKeys.Count, "Ensure all keystrokes are returned");
This compiles fine, but when I execute the code, I get the following Exception:
Test threw Exception:
FakeItEasy.Configuration.FakeConfigurationException:
The current proxy generator can not intercept the specified method for the following reason:
- Non virtual methods can not be intercepted.
If I change how I create the fake which then works without an exception, I can not get different values for the various calls to the same function.
A.CallTo(FakeINI).Where( x => x.Method.Name == "ReadString").WithReturnType<string>().Returns("N");
The above method does not allow me to control the return for the different calls that the function uses to the INI.
How can I combine the two methods, the where and the test of the parameters?
Additional definitions as requested:
public abstract class ConfigurationSettings
{
...
abstract public int ReadInteger(string Section, string Key, int Default);
abstract public string ReadString(string Section, string Key, string Default);
public int ReadInteger(string Section, string Key)
{ return ReadInteger(Section, Key, 0); }
public int ReadInteger(string Key, int Default)
{ return ReadInteger("", Key, Default); }
public int ReadInteger(string Key)
{ return ReadInteger(Key, 0); }
public string ReadString(string Section, string Key)
{ return ReadString(Section, Key, null); }
}
public class IniReader : ConfigurationSettings
{
...
public IniReader()
{
}
public IniReader(string PathAndFile)
{
this.PathAndFileName = PathAndFile;
}
public override int ReadInteger(string Section, string Key, int Default)
{
return GetPrivateProfileInt(Section, Key, Default, PathAndFileName);
}
public override string ReadString(string Section, string Key, string Default)
{
StringBuilder WorkingString = new StringBuilder(MAX_ENTRY);
int Return = GetPrivateProfileString(Section, Key, Default, WorkingString, MAX_ENTRY, PathAndFileName);
return WorkingString.ToString();
}
}
You're getting the
The current proxy generator can not intercept the specified method for the following reason:
- Non virtual methods can not be intercepted.
error because you're trying to fake the 2-parameter version of ReadString. Only virtual members, abstract members, or interface members can be faked. Since your two-paremter ReadString is none of these, it can't be faked. I think you should either have a virtual 2-parameter ReadString or fake the 3-parameter ReadString.
Your example pushes me towards faking the 3-parameter ReadString, especially since GetLoginSequence uses that one. Then I think you could just constrain using the expressions (rather than method name strings) and it would all work out.
I made a little test with bits of your code (mostly from before your update) and had success with faking the 3-parameter ReadString:
[Test]
public void BlairTest()
{
IniReader FakeINI = A.Fake<IniReader>();
A.CallTo(() => FakeINI.ReadString("SignOn", "SendEnterClear", A<string>._)).Returns("N");
A.CallTo(() => FakeINI.ReadString("SignOn", "SignOnKeyFirst", A<string>._)).Returns("N");
// Personally, I'd use the above syntax for this one too, but I didn't
// want to muck too much.
A.CallTo(FakeINI).Where(x => x.Method.Name == "ReadInteger").WithReturnType<int>().Returns(1000);
TestFile TestFileObject = new TestFile(FakeINI);
List<string> ReturnedKeys = TestFileObject.GetLoginSequence();
Assert.AreEqual(2, ReturnedKeys.Count, "Ensure all keystrokes are returned");
}
Related
I need help with building filter for MongoCollection of class A when I have filters for class B
public class A
{
public string ExampleAProperty { get; set; }
public B NestedB { get; set; }
public ICollection<B> NestedBCollection { get; set; }
}
public class B
{
public string ExampleBProperty { get; set; }
}
public class SearchClass
{
public async Task<ICollection<A>> SearchAsync(IMongoCollection<A> collection)
{
// this is just simple example of possible dozens predefined filters for B class.
// see FilterProvider logic used now
// var bFilter = Builders<B>.Filter.Eq(x => x.ExampleBProperty, "Example");
// in need filter A where B meets provided filters
var cursor = await collection.FindAsync(
Builders<A>.Filter.And(
// predefined filters are easy to reuse with array of elements
Builders<A>.Filter.ElemMatch(x => x.NestedBCollection, FilterProvider.SearchValue("Oleh")),
// but i did not found how to do this with single nested element
Builders<A>.Filter.Eq(x => x.NestedB, FilterProvider.SearchValue("Oleh")) // how?
)
);
return await cursor.ToListAsync();
}
}
// statics is not good but just for working example :)
public static class FilterProvider
{
public static FilterDefinition<B> SearchValue(string? value)
{
var builder = Builders<B>.Filter;
// if value is null show all
if (string.IsNullOrEmpty(value))
{
return builder.Empty;
}
// if value is "Oleh" search for "Amir"
if (value == "Oleh")
{
value = "Amir";
}
// any other additional logic to compose proper filter
// this could be search by serveral properties and so on
// however just for example i will search by hashed value :)
value = value.GetHashCode().ToString();
return builder.Eq(x => x.ExampleBProperty, value);
}
}
Please DON'T
use IMongoQueryable
propose to filter nested element by duplicate code (like x => x.NestedB.ExampleBProperty = "something")
UPDATED: example for Amir with explanation why p.2 is not than case and why code will be is duplicated if you his approach :)
As you may see we have complex (but very simple in current example) filter of data in B class. If you will use your approach - logic of composing filter for B (specified in FilterProvider.SearchValue) will be duplicated.
Thank you
You can easily do this:
public async Task<ICollection<A>> SearchAsync(IMongoCollection<A> collection, string search)
{
var bFilter = Builders<B>.Filter.Eq(x => x.ExampleBProperty, search);
var cursor = await collection.FindAsync(
Builders<A>.Filter.And(
Builders<A>.Filter.ElemMatch(x => x.NestedBCollection, bFilter),
Builders<A>.Filter.Eq(x => x.NestedB.ExampleBProperty, search)
)
);
return await cursor.ToListAsync();
}
I have a project in Asp.Net Core. This project has a ICacheService as below:
public interface ICacheService
{
T Get<T>(string key);
T Get<T>(string key, Func<T> getdata);
Task<T> Get<T>(string key, Func<Task<T>> getdata);
void AddOrUpdate(string key, object value);
}
The implementation is simply based on ConcurrentDictionary<string, object>, so its not that complicated, just storing and retrieving data from this dictionary. At one of my services I have a method as below:
public async Task<List<LanguageInfoModel>> GetLanguagesAsync(string frontendId, string languageId, string accessId)
{
async Task<List<LanguageInfoModel>> GetLanguageInfoModel()
{
var data = await _commonServiceProxy.GetLanguages(frontendId, languageId, accessId);
return data;
}
_scheduler.ScheduleAsync($"{CacheKeys.Jobs.LanguagesJob}_{frontendId}_{languageId}_{accessId}", async () =>
{
_cacheService.AddOrUpdate($"{CacheKeys.Languages}_{frontendId}_{languageId}_{accessId}", await GetLanguageInfoModel());
return JobStatus.Success;
}, TimeSpan.FromMinutes(5.0));
return await _cacheService.Get($"{CacheKeys.Languages}_{frontendId}_{languageId}_{accessId}", async () => await GetLanguageInfoModel());
}
The problem is that I have three params in this method that I use as a cache key. This works fine but the problem is that the combination of three params is pretty high so there will be so many duplication of objects in cache. I was thinking to create a cache without duplication like below:
To have a cache with a list as a key where I can store more than one key for one object. So when I get new elements I will check for each of them if it is in the cache, if it is in the cache I will only add a key in the key list otherwise insert a new element in the cache. The problem here is that testing if an object is in the cache is a big problem. I think it will consume a lot of resources and would need some serialization into a specific form to make the comparison possible which will make again the comparison consuming a lot of resources.
The cache might look something like this CustomDictionary<List<string>, object>
Does anybody know a good approach of solving this issue to not duplicate objects in the cache ?
EDIT 1:
My main concern is when I retrieve List<MyModel> from my webservices because they might have 80% of the objects with the same data which will drastically increase the size in memory. But this would be relevant for simple cases as well.
Lest suppose I have something like this:
MyClass o1 = new MyObject();
_cache.Set("key1", o1);
_cashe.Set("key2", o1);
In this case when trying to add the same object twice I would like to not duplicate it but to have key2 somehow pointing to the same object as key1. If this achieved it will be problem to invalidate them but I expect to have something like this:
_cache.Invalidate("key2");
This will check if there is another key pointing to same object. If so, it will only remove the key otherwise destroy the object itself.
Maybe we could reformulate this problem to two separate issues ...
executing the call for each combination and
storing n times the identical result, wasting tons of memory
For 1 I don't have any idea how we could prevent it, as we do not know prior to execution if we will fetch a duplicate in this setup. We would need more information that is based on when these values vary, which may or may not be possible.
For 2 one solution would be to override hashcode so it is based on the actual returned values. A good solution would be generic and walk through the object tree (which probably can be expensive). Would like to know if there are any pre-made solutions for this actually.
This answer is specifically for returning List<TItem>s, rather than just individual TItems, and it avoids duplication of any TItem as well as any List<T>. It uses arrays, because you're trying to save memory, and arrays will use less than a List.
Note that for this (and any solution really) to work, you MUST override Equals and GetHashCode on TItem, so that it knows what a duplicate item is. (Unless the data provider is returning the same object each time, which is unlikely.) If you don't have control of TItem, but you can yourself determine whether two TItems are equal, you can use an IEqualityComparer to do this, but the below solution would need to be modified very slightly in order to do that.
View the solution with a basic test at:
https://dotnetfiddle.net/pKHLQP
public class DuplicateFreeCache<TKey, TItem> where TItem : class
{
private ConcurrentDictionary<TKey, int> Primary { get; } = new ConcurrentDictionary<TKey, int>();
private List<TItem> ItemList { get; } = new List<TItem>();
private List<TItem[]> ListList { get; } = new List<TItem[]>();
private Dictionary<TItem, int> ItemDict { get; } = new Dictionary<TItem, int>();
private Dictionary<IntArray, int> ListDict { get; } = new Dictionary<IntArray, int>();
public IReadOnlyList<TItem> GetOrAdd(TKey key, Func<TKey, IEnumerable<TItem>> getFunc)
{
int index = Primary.GetOrAdd(key, k =>
{
var rawList = getFunc(k);
lock (Primary)
{
int[] itemListByIndex = rawList.Select(item =>
{
if (!ItemDict.TryGetValue(item, out int itemIndex))
{
itemIndex = ItemList.Count;
ItemList.Add(item);
ItemDict[item] = itemIndex;
}
return itemIndex;
}).ToArray();
var intArray = new IntArray(itemListByIndex);
if (!ListDict.TryGetValue(intArray, out int listIndex))
{
lock (ListList)
{
listIndex = ListList.Count;
ListList.Add(itemListByIndex.Select(ii => ItemList[ii]).ToArray());
}
ListDict[intArray] = listIndex;
}
return listIndex;
}
});
lock (ListList)
{
return ListList[index];
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"A cache with:");
sb.AppendLine($"{ItemList.Count} unique Items;");
sb.AppendLine($"{ListList.Count} unique lists of Items;");
sb.AppendLine($"{Primary.Count} primary dictionary items;");
sb.AppendLine($"{ItemDict.Count} item dictionary items;");
sb.AppendLine($"{ListDict.Count} list dictionary items;");
return sb.ToString();
}
//We have this to make Dictionary lookups on int[] find identical arrays.
//One could also just make an IEqualityComparer, but I felt like doing it this way.
public class IntArray
{
private readonly int _hashCode;
public int[] Array { get; }
public IntArray(int[] arr)
{
Array = arr;
unchecked
{
_hashCode = 0;
for (int i = 0; i < arr.Length; i++)
_hashCode = (_hashCode * 397) ^ arr[i];
}
}
protected bool Equals(IntArray other)
{
return Array.SequenceEqual(other.Array);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((IntArray)obj);
}
public override int GetHashCode() => _hashCode;
}
}
It occurred to me that a ReaderWriterLockSlim would be better than the lock(ListList), if the lock is causing performance to lag, but it's very slightly more complicated.
Similar to #MineR, this solution is performing a 'double caching' operation: it caches the key'ed lists (lookups) as well as the individual objects - performing an automatic deduplication.
It is a fairly simple solution using two ConcurrentDictionaries - one acting as a HashSet and one as a keyed lookup. This allows most of the threading concerns to be handled by the framework.
You can also pass in and share the hashset between multiple Cachedlookups allowing lookups with different keys.
Note that object equality or an IEqualityComparer are required to make any such solution function.
Class:
public class CachedLookup<T, TKey>
{
private readonly ConcurrentDictionary<T, T> _hashSet;
private readonly ConcurrentDictionary<TKey, List<T>> _lookup = new ConcurrentDictionary<TKey, List<T>>();
public CachedLookup(ConcurrentDictionary<T, T> hashSet)
{
_hashSet = hashSet;
}
public CachedLookup(IEqualityComparer<T> equalityComparer = default)
{
_hashSet = equalityComparer is null ? new ConcurrentDictionary<T, T>() : new ConcurrentDictionary<T, T>(equalityComparer);
}
public List<T> Get(TKey key) => _lookup.ContainsKey(key) ? _lookup[key] : null;
public List<T> Get(TKey key, Func<TKey, List<T>> getData)
{
if (_lookup.ContainsKey(key))
return _lookup[key];
var result = DedupeAndCache(getData(key));
_lookup.TryAdd(key, result);
return result;
}
public async ValueTask<List<T>> GetAsync(TKey key, Func<TKey, Task<List<T>>> getData)
{
if (_lookup.ContainsKey(key))
return _lookup[key];
var result = DedupeAndCache(await getData(key));
_lookup.TryAdd(key, result);
return result;
}
public void Add(T value) => _hashSet.TryAdd(value, value);
public List<T> AddOrUpdate(TKey key, List<T> data)
{
var deduped = DedupeAndCache(data);
_lookup.AddOrUpdate(key, deduped, (k,l)=>deduped);
return deduped;
}
private List<T> DedupeAndCache(IEnumerable<T> input) => input.Select(v => _hashSet.GetOrAdd(v,v)).ToList();
}
Example Usage:
public class ExampleUsage
{
private readonly CachedLookup<LanguageInfoModel, (string frontendId, string languageId, string accessId)> _lookup
= new CachedLookup<LanguageInfoModel, (string frontendId, string languageId, string accessId)>(new LanguageInfoModelComparer());
public ValueTask<List<LanguageInfoModel>> GetLanguagesAsync(string frontendId, string languageId, string accessId)
{
return _lookup.GetAsync((frontendId, languageId, accessId), GetLanguagesFromDB(k));
}
private async Task<List<LanguageInfoModel>> GetLanguagesFromDB((string frontendId, string languageId, string accessId) key) => throw new NotImplementedException();
}
public class LanguageInfoModel
{
public string FrontendId { get; set; }
public string LanguageId { get; set; }
public string AccessId { get; set; }
public string SomeOtherUniqueValue { get; set; }
}
public class LanguageInfoModelComparer : IEqualityComparer<LanguageInfoModel>
{
public bool Equals(LanguageInfoModel x, LanguageInfoModel y)
{
return (x?.FrontendId, x?.AccessId, x?.LanguageId, x?.SomeOtherUniqueValue)
.Equals((y?.FrontendId, y?.AccessId, y?.LanguageId, y?.SomeOtherUniqueValue));
}
public int GetHashCode(LanguageInfoModel obj) =>
(obj.FrontendId, obj.LanguageId, obj.AccessId, obj.SomeOtherUniqueValue).GetHashCode();
}
Notes:
The CachedLookup class is generic on both the value and key. The example use of ValueTuple makes it easy to have compound keys. I have also used ValueTuples to simplify the equality comparisons.
This usage of ValueTask fits nicely with its intended purpose, returning the cached list synchronously.
If you have access to the lower level data access layer, one optimization would be to move the deduplication to happen before the objects are instantiated (based on property value equality). This would reduce the allocations and load on the GC.
If you have control over your complete solution then you can do something like this.
Whatever object that is capable of storing in Cache. You have to identify that.
All Such object implement common interface.
public interface ICacheable
{
string ObjectId(); // This will implement logic to calculate each object identity. You can count hash code but you have to add some other value to.
}
Now when you store object in Cache. You do two thing.
Store Two way things. Like one cache store ObjectId to Key.
Another will contains ObjectId to Object.
Overall idea is that when you get object. You search in first cache and see that the key you want is there against ObjectId. If yes then no further action otherwise you have to create new entry in First Cache for ObjectId to Key Map.
If object is not present then you have to create entry in both cache
Note : You have to overcome performance issue. Because your keys is some kind of list so it create problem while searching.
It sound to me as though you need to implement some sort of index. Assuming that your model is fairly large, which is why you want to save memory then you could do this with two concurrent dictionaries.
The first would be ConcurrentDictionary<string, int> (or whatever unique id applies to your model object) and would contain your key values. Each key is obviously be different as per all your combinations, but you are only duplicating the int unique key for all of your objects, not the entire object.
The second dictionary would be a ConcurrentDictionary<int, object> or ConcurrentDictionary<int, T> and would contain your unique large objects indexed via their unique key.
When building the cache you would need to populate both dictionaries, the exact method would depend upon how you are doing it at the moment.
To retrieve an object you would build the key as you do at the moment, retrieve the hashcode value from the first dictionary, and then use that to locate the actual object from the second dictionary.
It is also possible to invalidate one key without invalidating the main object another key is also using it, although it does require you to iterate over the index dictionary to check if any other key is pointing to the same object.
I think this is not a caching concern where one key map to one and only one data. Yours is not in this case. You are trying to manipulate a local data repository in memory work as cached data.
You are trying to create mappers between keys and objects that loaded from remote. One key is able to map to many objects. One object can be mapped by many Keys, so the relationship is n <======> n
I have created a sample modal as following
Key, KeyMyModel and MyModel are classes for caching handler
RemoteModel is class that you got from remote service
With this models, you are able to meet the requirements. This utilizes entity Id to specify an object, does not need to hash to specify duplications. This is very basic that I have implemented set method. Invaildate a key is very similar. You must write code that ensure thread safe as well
public class MyModel
{
public RemoteModel RemoteModel { get; set; }
public List<KeyMyModel> KeyMyModels { get; set; }
}
public class RemoteModel
{
public string Id { get; set; } // Identity property this get from remote service
public string DummyProperty { get; set; } // Some properties returned by remote service
}
public class KeyMyModel
{
public string Key { get; set; }
public string MyModelId { get; set; }
}
public class Key
{
public string KeyStr { get; set; }
public List<KeyMyModel> KeyMyModels { get; set; }
}
public interface ICacheService
{
List<RemoteModel> Get(string key);
List<RemoteModel> Get(string key, Func<List<RemoteModel>> getdata);
Task<List<RemoteModel>> Get(string key, Func<Task<List<RemoteModel>>> getdata);
void AddOrUpdate(string key, object value);
}
public class CacheService : ICacheService
{
public List<MyModel> MyModels { get; private set; }
public List<Key> Keys { get; private set; }
public List<KeyMyModel> KeyMyModels { get; private set; }
public CacheService()
{
MyModels = new List<MyModel>();
Keys = new List<Key>();
KeyMyModels = new List<KeyMyModel>();
}
public List<RemoteModel> Get(string key)
{
return MyModels.Where(s => s.KeyMyModels.Any(t => t.Key == key)).Select(s => s.RemoteModel).ToList();
}
public List<RemoteModel> Get(string key, Func<List<RemoteModel>> getdata)
{
var remoteData = getdata();
Set(key, remoteData);
return MyModels.Where(s => s.KeyMyModels.Any(t => t.Key == key)).Select(t => t.RemoteModel).ToList();
}
public Task<List<RemoteModel>> Get(string key, Func<Task<List<RemoteModel>>> getdata)
{
throw new NotImplementedException();
}
public void AddOrUpdate(string key, object value)
{
throw new NotImplementedException();
}
public void Invalidate(string key)
{
}
public void Set(string key, List<RemoteModel> data)
{
var Key = Keys.FirstOrDefault(s => s.KeyStr == key) ?? new Key()
{
KeyStr = key
};
foreach (var remoteModel in data)
{
var exist = MyModels.FirstOrDefault(s => s.RemoteModel.Id == remoteModel.Id);
if (exist == null)
{
// add data to the cache
var myModel = new MyModel()
{
RemoteModel = remoteModel
};
var keyMyModel = new KeyMyModel()
{
Key = key,
MyModelId = remoteModel.Id
};
myModel.KeyMyModels.Add(keyMyModel);
Key.KeyMyModels.Add(keyMyModel);
Keys.Add(Key);
}
else
{
exist.RemoteModel = remoteModel;
var existKeyMyModel =
KeyMyModels.FirstOrDefault(s => s.Key == key && s.MyModelId == exist.RemoteModel.Id);
if (existKeyMyModel == null)
{
existKeyMyModel = new KeyMyModel()
{
Key = key,
MyModelId = exist.RemoteModel.Id
};
Key.KeyMyModels.Add(existKeyMyModel);
exist.KeyMyModels.Add(existKeyMyModel);
KeyMyModels.Add(existKeyMyModel);
}
}
}
// Remove MyModels if need
var remoteIds = data.Select(s => s.Id);
var currentIds = KeyMyModels.Where(s => s.Key == key).Select(s => s.MyModelId);
var removingIds = currentIds.Except(remoteIds);
var removingKeyMyModels = KeyMyModels.Where(s => s.Key == key && removingIds.Any(i => i == s.MyModelId)).ToList();
removingKeyMyModels.ForEach(s =>
{
KeyMyModels.Remove(s);
Key.KeyMyModels.Remove(s);
});
}
}
class CacheConsumer
{
private readonly CacheService _cacheService = new CacheService();
public List<RemoteModel> GetMyModels(string frontendId, string languageId, string accessId)
{
var key = $"{frontendId}_{languageId}_{accessId}";
return _cacheService.Get(key, () =>
{
// call to remote service here
return new List<RemoteModel>();
});
}
}
I'm writing a validation engine.
I'm given with an a object payload containing around list of ~40 different properties. Every property will undergo different validation.
The validations include checking if the field is a string and validating if the length exceeds permissible limit set by db and there are conditions to check null values and empty fields as well.
So, I thought of picking the strategy pattern.
Code:
interface IValidationEngine
{
Error Validate(string propName, dynamic propValue);
}
public class StringLengthValidator : IValidationEngine
{
static Dictionary<string, int> sLengths = new Dictionary<string, int>();
public StringLengthValidator()
{
if (sLengths.Count == 0)
{
sLengths = new Dictionary<string, int>(){....};
}
}
public Error Validate(string name, dynamic value)
{
var err = default(Error);
//logic here
return err;
}
}
public class MandatoryValidator : IValidationEngine
{
public Error Validate(string name, dynamic value)
{
var err = default(Error);
//logic here
return err;
}
}
public class MandatoryStringLengthValidator : IValidationEngine
{
public Error Validate(string name, dynamic value)
{
var e = default(Error);
if (value == null || (value.GetType() == typeof(string) && string.IsNullOrWhiteSpace(value)))
{
e = new Error()
{
//.. err info
};
}
else
{
StringLengthValidator sl = new StringLengthValidator();
e = sl.Validate(name, value);
}
return e;
}
}
There is another class which I named it ValidationRouter. The job of this is to route to the specific validator (Think of this as a Strategy as per pattern).
I wrote the initial router to have a dictionary of keys and its respective objects map so that whatever comes in will match its key and calls its appropriate validate() method on the dictionary value. But as adding keys and values with new object creation. I think the class will take lot of memory as I've the object creation in the constructor.
So, I've added Lazy to the object creation and it ended up like this
Here is the code for router
public class ValidationRouter
{
public Dictionary<string, Lazy<IValidationEngine>> strategies = new Dictionary<string, Lazy<IValidationEngine>>();
public ValidationRouter()
{
var mandatoryStringLength = new Lazy<IValidationEngine>(() => new MandatoryStringLengthValidator());
var mandatory = new Lazy<IValidationEngine>(() => new MandatoryValidator());
var stringLength = new Lazy<IValidationEngine>(() => new StringLengthValidator());
strategies.Add("position", mandatoryStringLength);
strategies.Add("pcode", stringLength);
strategies.Add("username", mandatoryStringLength);
strategies.Add("description", stringLength);
strategies.Add("sourcename", stringLength);
//OMG: 35 more fields to be added to route to specific routers
}
public Error Execute(string name, dynamic value)
{
//Find appropriate field and route to its strategy
var lowered = name.ToLower();
if (!strategies.ContainsKey(lowered)) return default(Error);
return strategies[lowered].Value.Validate(name, value);
}
}
As you can see I need to add 35 more keys to dictionaries with appropriate strategies in the constructor.
Is the approach correct or is there any better way of routing to specific validator algorithms ?
I thought of object creation to be done by factory pattern with Activator.CreateInstance but not sure how to achieve as each of my properties will have different strategies.
Any ideas would be appreciated.
Xunit has a nice feature: you can create one test with a Theory attribute and put data in InlineData attributes, and xUnit will generate many tests, and test them all.
I want to have something like this, but the parameters to my method are not 'simple data' (like string, int, double), but a list of my class:
public static void WriteReportsToMemoryStream(
IEnumerable<MyCustomClass> listReport,
MemoryStream ms,
StreamWriter writer) { ... }
There are many xxxxData attributes in XUnit. Check out for example the MemberData attribute.
You can implement a property that returns IEnumerable<object[]>. Each object[] that this method generates will be then "unpacked" as a parameters for a single call to your [Theory] method.
See i.e. these examples from here
Here are some examples, just for a quick glance.
MemberData Example: just here at hand
public class StringTests2
{
[Theory, MemberData(nameof(SplitCountData))]
public void SplitCount(string input, int expectedCount)
{
var actualCount = input.Split(' ').Count();
Assert.Equal(expectedCount, actualCount);
}
public static IEnumerable<object[]> SplitCountData =>
new List<object[]>
{
new object[] { "xUnit", 1 },
new object[] { "is fun", 2 },
new object[] { "to test with", 3 }
};
}
XUnit < 2.0: Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.
ClassData Example
public class StringTests3
{
[Theory, ClassData(typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
public IEnumerator<object[]> GetEnumerator()
{ return _data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
}
XUnit >= 2.0: Instead of ClassData, now there's an 'overload' of [MemberData] that allows to use static members from other classes. Examples below have been updated to use it, since XUnit < 2.x is pretty ancient now.
Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.
MemberData Example: look there to another type
public class StringTests3
{
[Theory, MemberData(nameof(IndexOfData.SplitCountData), MemberType = typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
public static IEnumerable<object[]> SplitCountData =>
new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
}
Disclaimer :)
Last time checked #20210903 with dotnetfiddle.net on C# 5.0 and xunit 2.4.1 .. and failed. I couldn't mix-in a test-runner into that fiddle. But at least it compiled fine. Note that this was originally written years ago, things changed a little. I fixed them according to my hunch and comments. So.. it may contain inobvious typos, otherwise obvious bugs that would instantly pop up at runtime, and traces of milk & nuts.
Suppose that we have a complex Car class that has a Manufacturer class:
public class Car
{
public int Id { get; set; }
public long Price { get; set; }
public Manufacturer Manufacturer { get; set; }
}
public class Manufacturer
{
public string Name { get; set; }
public string Country { get; set; }
}
We're going to fill and pass the Car class to a Theory test.
So create a 'CarClassData' class that returns an instance of the Car class like below:
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="country",
Name="name"
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
It's time for creating a test method(CarTest) and define the car as a parameter:
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(Car car)
{
var output = car;
var result = _myRepository.BuyCar(car);
}
**If you're going to pass a list of car objects to Theory then change the CarClassData as follow:
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new List<Car>()
{
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="Iran",
Name="arya"
}
},
new Car
{
Id=2,
Price=45000,
Manufacturer = new Manufacturer
{
Country="Torbat",
Name="kurosh"
}
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
And the theory will be:
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(List<Car> cars)
{
var output = cars;
}
Good Luck
To update #Quetzalcoatl's answer: The attribute [PropertyData] has been superseded by [MemberData] which takes as argument the string name of any static method, field, or property that returns an IEnumerable<object[]>. (I find it particularly nice to have an iterator method that can actually calculate test cases one at a time, yielding them up as they're computed.)
Each element in the sequence returned by the enumerator is an object[] and each array must be the same length and that length must be the number of arguments to your test case (annotated with the attribute [MemberData] and each element must have the same type as the corresponding method parameter. (Or maybe they can be convertible types, I don't know.)
(See release notes for xUnit.net March 2014 and the actual patch with example code.)
Creating anonymous object arrays is not the easiest way to construct the data so I used this pattern in my project.
First define some reusable, shared classes:
//http://stackoverflow.com/questions/22093843
public interface ITheoryDatum
{
object[] ToParameterArray();
}
public abstract class TheoryDatum : ITheoryDatum
{
public abstract object[] ToParameterArray();
public static ITheoryDatum Factory<TSystemUnderTest, TExpectedOutput>(TSystemUnderTest sut, TExpectedOutput expectedOutput, string description)
{
var datum= new TheoryDatum<TSystemUnderTest, TExpectedOutput>();
datum.SystemUnderTest = sut;
datum.Description = description;
datum.ExpectedOutput = expectedOutput;
return datum;
}
}
public class TheoryDatum<TSystemUnderTest, TExpectedOutput> : TheoryDatum
{
public TSystemUnderTest SystemUnderTest { get; set; }
public string Description { get; set; }
public TExpectedOutput ExpectedOutput { get; set; }
public override object[] ToParameterArray()
{
var output = new object[3];
output[0] = SystemUnderTest;
output[1] = ExpectedOutput;
output[2] = Description;
return output;
}
}
Now your individual test and member data is easier to write and cleaner...
public class IngredientTests : TestBase
{
[Theory]
[MemberData(nameof(IsValidData))]
public void IsValid(Ingredient ingredient, bool expectedResult, string testDescription)
{
Assert.True(ingredient.IsValid == expectedResult, testDescription);
}
public static IEnumerable<object[]> IsValidData
{
get
{
var food = new Food();
var quantity = new Quantity();
var data= new List<ITheoryDatum>();
data.Add(TheoryDatum.Factory(new Ingredient { Food = food } , false, "Quantity missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity } , false, "Food missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity, Food = food } , true, "Valid" ));
return data.ConvertAll(d => d.ToParameterArray());
}
}
}
The string Description property is to throw yourself a bone when one of your many test cases fail.
You can try this way:
public class TestClass {
bool isSaturday(DateTime dt)
{
string day = dt.DayOfWeek.ToString();
return (day == "Saturday");
}
[Theory]
[MemberData("IsSaturdayIndex", MemberType = typeof(TestCase))]
public void test(int i)
{
// parse test case
var input = TestCase.IsSaturdayTestCase[i];
DateTime dt = (DateTime)input[0];
bool expected = (bool)input[1];
// test
bool result = isSaturday(dt);
result.Should().Be(expected);
}
}
Create another class to hold the test data:
public class TestCase
{
public static readonly List<object[]> IsSaturdayTestCase = new List<object[]>
{
new object[]{new DateTime(2016,1,23),true},
new object[]{new DateTime(2016,1,24),false}
};
public static IEnumerable<object[]> IsSaturdayIndex
{
get
{
List<object[]> tmp = new List<object[]>();
for (int i = 0; i < IsSaturdayTestCase.Count; i++)
tmp.Add(new object[] { i });
return tmp;
}
}
}
For my needs I just wanted to run a series of 'test users' through some tests - but [ClassData] etc. seemed overkill for what I needed (because the list of items was localized to each test).
So I did the following, with an array inside the test - indexed from the outside:
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task Account_ExistingUser_CorrectPassword(int userIndex)
{
// DIFFERENT INPUT DATA (static fake users on class)
var user = new[]
{
EXISTING_USER_NO_MAPPING,
EXISTING_USER_MAPPING_TO_DIFFERENT_EXISTING_USER,
EXISTING_USER_MAPPING_TO_SAME_USER,
NEW_USER
} [userIndex];
var response = await Analyze(new CreateOrLoginMsgIn
{
Username = user.Username,
Password = user.Password
});
// expected result (using ExpectedObjects)
new CreateOrLoginResult
{
AccessGrantedTo = user.Username
}.ToExpectedObject().ShouldEqual(response);
}
This achieved my goal, while keeping the intent of the test clear. You just need to keep the indexes in sync but that's all.
Looks nice in the results, it's collapsable and you can rerun a specific instance if you get an error:
This is how I solved your problem, I had the same scenario. So inline with custom objects and a different number of objects on each run.
[Theory]
[ClassData(typeof(DeviceTelemetryTestData))]
public async Task ProcessDeviceTelemetries_TypicalDeserialization_NoErrorAsync(params DeviceTelemetry[] expected)
{
// Arrange
var timeStamp = DateTimeOffset.UtcNow;
mockInflux.Setup(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>())).ReturnsAsync("Success");
// Act
var actual = await MessageProcessingTelemetry.ProcessTelemetry(JsonConvert.SerializeObject(expected), mockInflux.Object);
// Assert
mockInflux.Verify(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>()), Times.Once);
Assert.Equal("Success", actual);
}
So this is my unit test, notice the params parameter. This allow to send a different number of object. And now my DeviceTelemetryTestData class :
public class DeviceTelemetryTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Hope it helps !
Despite this has already been answered I just want to add an improvement here.
The restriction of passing objects in InlineData attribute is not a limitiation of xUnit itself but C# attributes.
See this compiler error: Compiler Error CS0182
xUnit.Sdk provides you with DataAttribute class that you could inherit and override its GetData method and use it to pass whatever you feel like you want..
I usually use it alongside DataTestBuilders pattern and build something like that..
public class ValidComplexObjectDataSource : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] {
ComplexObjectBuilder
.BasicComplexObject()
.Build()
};
yield return new object[] {
ComplexObjectBuilder
.BasicComplexObject()
.WithoutSomeAttribute()
.Build()
};
// ... list all test cases you want to pass to your method
}
}
This ComplexObjectBuilder could be whatever your object is, highly recommend checking builder pattern
[Theory]
[Trait("Validation", "CreateXYZCommand")]
[ValidComplexObjectDataSource]
public void CreateXYZCommandValidator_WithValidInput_ShouldPassAllValidations(CreateComplexObjectInput createComplexObjectInput)
{
var command = new CreateXYZCommand(createComplexObjectInput);
var result = _validator.TestValidate(command);
result.ShouldNotHaveAnyValidationErrors();
}
I only demonstrated it with a single object, you have an array of objects you can yield.
yield return new object[] {
ComplexObject_1,
ComplexObject_2,
string_attribute,
int_attribute
};
and have these as arguments to your test cases.
You can utilize TheoryData for complex types like classes.
[Theory, MemberData(nameof(CustomClassTests))]
public async Task myTestName(MyCustomClass customClassTestData) { ... }
public record MyCustomClass { ... }
public static TheoryData<MyCustomClass> CustomClassTests {
get {
return new() {
new MyCustomClass{ ... },
new MyCustomClass{ ... },
...
};
}
}
I guess you mistaken here. What xUnit Theory attribute actually means: You want to test this function by sending special/random values as parameters that this function-under-test receives. That means that what you define as the next attribute, such as: InlineData, PropertyData, ClassData, etc.. will be the source for those parameters. That means that you should construct the source object to provide those parameters. In your case I guess you should use ClassData object as source. Also - please note that ClassData inherits from: IEnumerable<> - that means each time another set of generated parameters will be used as incoming parameters for function-under-test until IEnumerable<> produces values.
Example here: Tom DuPont .NET
Example may be incorrect - I didn't use xUnit for a long time
I would like to get property name when I'm in it via reflection. Is it possible?
I have code like this:
public CarType Car
{
get { return (Wheel) this["Wheel"];}
set { this["Wheel"] = value; }
}
And because I need more properties like this I would like to do something like this:
public CarType Car
{
get { return (Wheel) this[GetThisPropertyName()];}
set { this[GetThisPropertyName()] = value; }
}
Since properties are really just methods you can do this and clean up the get_ returned:
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var x = p.Something;
Console.ReadLine();
}
public string Something
{
get
{
return MethodBase.GetCurrentMethod().Name;
}
}
}
If you profile the performance you should find MethodBase.GetCurrentMethod() is miles faster than StackFrame. In .NET 1.1 you will also have issues with StackFrame in release mode (from memory I think I found it was 3x faster).
That said I'm sure the performance issue won't cause too much of a problem- though an interesting discussion on StackFrame slowness can be found here.
I guess another option if you were concerned about performance would be to create a Visual Studio Intellisense Code Snippet that creates the property for you and also creates a string that corresponds to the property name.
Slightly confusing example you presented, unless I just don't get it.
From C# 6.0 you can use the nameof operator.
public CarType MyProperty
{
get { return (CarType)this[nameof(MyProperty)]};
set { this[nameof(MyProperty)] = value]};
}
If you have a method that handles your getter/setter anyway, you can use the C# 4.5 CallerMemberName attribute, in this case you don't even need to repeat the name.
public CarType MyProperty
{
get { return Get<CarType>(); }
set { Set(value); }
}
public T Get<T>([CallerMemberName]string name = null)
{
return (T)this[name];
}
public void Set<T>(T value, [CallerMemberName]string name = null)
{
this[name] = value;
}
I'd like to know more about the context in which you need it since it seems to me that you should already know what property you are working with in the property accessor. If you must, though, you could probably use MethodBase.GetCurrentMethod().Name and remove anything after get_/set_.
Update:
Based on your changes, I would say that you should use inheritance rather than reflection. I don't know what data is in your dictionary, but it seems to me that you really want to have different Car classes, say Sedan, Roadster, Buggy, StationWagon, not keep the type in a local variable. Then you would have implementations of methods that do the proper thing for that type of Car. Instead of finding out what kind of car you have, then doing something, you then simply call the appropriate method and the Car object does the right thing based on what type it is.
public interface ICar
{
void Drive( decimal velocity, Orientation orientation );
void Shift( int gear );
...
}
public abstract class Car : ICar
{
public virtual void Drive( decimal velocity, Orientation orientation )
{
...some default implementation...
}
public abstract void Shift( int gear );
...
}
public class AutomaticTransmission : Car
{
public override void Shift( int gear )
{
...some specific implementation...
}
}
public class ManualTransmission : Car
{
public override void Shift( int gear )
{
...some specific implementation...
}
}
Use MethodBase.GetCurrentMethod() instead!
Reflection is used to do work with types that can't be done at compile time. Getting the name of the property accessor you're in can be decided at compile time so you probably shouldn't use reflection for it.
You get use the accessor method's name from the call stack using System.Diagnostics.StackTrace though.
string GetPropertyName()
{
StackTrace callStackTrace = new StackTrace();
StackFrame propertyFrame = callStackTrace.GetFrame(1); // 1: below GetPropertyName frame
string properyAccessorName = propertyFrame.GetMethod().Name;
return properyAccessorName.Replace("get_","").Replace("set_","");
}
FWIW I implemented a system like this:
[CrmAttribute("firstname")]
public string FirstName
{
get { return GetPropValue<string>(MethodBase.GetCurrentMethod().Name); }
set { SetPropValue(MethodBase.GetCurrentMethod().Name, value); }
}
// this is in a base class, skipped that bit for clairty
public T GetPropValue<T>(string propName)
{
propName = propName.Replace("get_", "").Replace("set_", "");
string attributeName = GetCrmAttributeName(propName);
return GetAttributeValue<T>(attributeName);
}
public void SetPropValue(string propName, object value)
{
propName = propName.Replace("get_", "").Replace("set_", "");
string attributeName = GetCrmAttributeName(propName);
SetAttributeValue(attributeName, value);
}
private static Dictionary<string, string> PropToAttributeMap = new Dictionary<string, string>();
private string GetCrmAttributeName(string propertyName)
{
// keyName for our propertyName to (static) CrmAttributeName cache
string keyName = this.GetType().Name + propertyName;
// have we already done this mapping?
if (!PropToAttributeMap.ContainsKey(keyName))
{
Type t = this.GetType();
PropertyInfo info = t.GetProperty(propertyName);
if (info == null)
{
throw new Exception("Cannot find a propety called " + propertyName);
}
object[] attrs = info.GetCustomAttributes(false);
foreach (object o in attrs)
{
CrmAttributeAttribute attr = o as CrmAttributeAttribute ;
if (attr != null)
{
// found it. Save the mapping for next time.
PropToAttributeMap[keyName] = attr.AttributeName;
return attr.AttributeName;
}
}
throw new Exception("Missing MemberOf attribute for " + info.Name + "." + propertyName + ". Could not auto-access value");
}
// return the existing mapping
string result = PropToAttributeMap[keyName];
return result;
}
There's also a custom attribute class called CrmAttributeAttribute.
I'd strongly recommend against using GetStackFrame() as part of your solution, my original version of the solution was originally the much neater:
return GetPropValue<string>();
But it was 600x slower than the version above.
Solution # 1
var a = nameof(SampleMethod); //a == SampleMethod
var b = nameof(SampleVariable); //b == SampleVariable
var c = nameof(SampleProperty); //c == SampleProperty
Solution # 2
MethodBase.GetCurrentMethod().Name; // Name of method in which you call the code
MethodBase.GetCurrentMethod().Name.Replace("set_", "").Replace("get_", ""); // current Property
Solution # 3
from StackTrace:
public static class Props
{
public static string CurrPropName =>
(new StackTrace()).GetFrame(1).GetMethod().Name.Replace("set_", "").Replace("get_", "");
public static string CurrMethodName =>
(new StackTrace()).GetFrame(1).GetMethod().Name;
}
you just need to call Props.CurrPropName or Props.CurrMethodName
Solution # 4
Solution for .NET 4.5+:
public static class Props
{
public static string GetCallerName([System.Runtime.CompilerServices.CallerMemberName] String propertyName = "")
{
return propertyName;
}
}
usage: Props.GetCallerName();
Yes, it is!
string test = "test string";
Type type = test.GetType();
PropertyInfo[] propInfos = type.GetProperties();
for (int i = 0; i < propInfos.Length; i++)
{
PropertyInfo pi = (PropertyInfo)propInfos.GetValue(i);
string propName = pi.Name;
}
Try using System.Diagnostics.StackTrace to reflect on the call stack. The property should be somewhere in the call stack (probably at the top if you're calling it directly from the property's code).