views:

972

answers:

3

One of our unit tests is to populate properties within our business objects with random data.

These properties are of different intrinsic types and therefore we would like to use the power of generics to return data of the type you pass in. Something along the lines of:

public static T GetData<T>()

How would you go about approaching this? Would a low level interface work? (IConvertible)

+11  A: 

You could keep the "easy to use" GetData interface you've got there, but internally have a Dictionary<Type, object> where each value is a Func<T> for the relevant type. GetData would then have an implementation such as:

public static T GetData<T>()
{
    object factory;
    if (!factories.TryGet(typeof(T), out factory))
    {
         throw new ArgumentException("No factory for type " + typeof(T).Name);
    }
    Func<T> factoryFunc = (Func<T>) factory;
    return factoryFunc();
}

You'd then set up the factory dictionary in a static initializer, with one delegate for each type of random data you wanted to create. In some cases you could use a simple lambda expression (e.g. for integers) and in some cases the delegate could point to a method doing more work (e.g. for strings).

You may wish to use my StaticRandom class for threads-safe RNG, by the way.

Jon Skeet
Interesting approach. Got my vote.
OregonGhost
Thanks, i'll give that a go and post back my results.
Adam Naylor
Just a question, why do you declare factory as object instead of Func<T> in the first place?
OregonGhost
Because the dictionary is declared to be Dictionary<Type, object> - and has to be, because you can't express that the type of a value in the dictionary depends on the key :(
Jon Skeet
(Actually, you could make it Dictionary<Type, Delegate> but it really doesn't matter much.)
Jon Skeet
The factory lookup might be improved a bit by implementing a recursive search. If you don't find a factory for the type T, try to get the factory for typeof(T).BaseType, etc. But I'm not sure it is really useful if you use this GetData method for base types...
Romain Verdier
Romain - Am i right in thinking that this wouldn't be useful for intrinsic types?
Adam Naylor
+2  A: 

It depends on what data you want to randomize, because the way or the algorithm you want to use is totally different depending on the type.

For example:

// Random int
Random r = new Random();
return r.Next();

// Random Guid
return Guid.NewGuid();

...

So this obviously makes the use of generics a nice semplification on the user's end, but it adds no value to the way you write the class. You could use a switch clause or a dictionary (like Jon Skeet suggests):

switch(typeof(T))
{
    case System.Int32:
        Random r = new Random();
        return (T)r.Next();
    case System.Guid:
        return (T)Guid.NewGuid();
    ...

Then you would use the class as you expect:

RandomGenerator.GetData<Guid>();
...
Sklivvz
In order to cast r.Next() to type T what would T inherit from?
Adam Naylor
I assume the cast would be resolved at runtime, but I haven't tried, if not you can double cast (T) (object) ...
Sklivvz
That pretty much worked, as the solution i was looking for was quite a straight forward one.Thanks
Adam Naylor
+2  A: 

In general I would avoid writing random unit tests, because this is just not the purpose of unit tests. While writing unit tests you really want to generate the data manually to make sure that all the paths in your class/program are covered, and usually you hardcode this data in the test, to make it possible to rerun the test.

So I guess you are really writing smoke tests, to see how your software behaves for big data sets. Here I think you should implement a specific generator for each of your business object types, as someone else already suggested - to make sure that the data is reasonably similar to what you expect in production (e.g. if IDs are sequential, then generate them sequential and not random).

Grzenio