1

I have an issue with a Windows Forms application that I am creating. The application is supposed to be an integration testing application. Essentially, it's supposed to test the methods that utilize lots of web services in one of my classes. I am loading the methods from the class I want to test via reflection, and am doing so like this:

private List<string> GetMethods(Type type)
{
    return (from method in type.GetMethods() where method.IsPublic &&
        method.ReturnType == typeof(void) select method.Name).ToList();
}

This returns a list of the methods from that class that have been created to test the web services and places them in a ListBox where the user can select as many methods as he/she likes. My confusion comes in here. What I would like to do is get the methods selected by the user and execute the corresponding method X amount of times (there is a text box for entering the number of times to execute a method on the form as well). I can't figure out how to execute these methods based on the name of the method I got through reflection. I've tried something like this, but I know it's not right:

private void RunMethods(Type type)
{
    var tester = new ClassToTest();
    foreach(var item in lstMethodList.SelectedItems)
    {
        foreach(var method in type.GetMethods())
        {
            if(String.Equals(item.ToString(), method.Name))
            {
                ThreadStart ts = new ThreadStart(method.Name);
                Thread thread1 = new Thread(ts);
                thread1.Start();
            }
         }
     }
}

This won't even compile, as a ThreadStart requires a method name as a parameter. Is there any way that this is possible to do? Maybe I'm going about it wrong logically, but I'd like to create a thread for each method that needs to be run and execute that method however many times the user specifies. This is supposed to be a way of doing integration testing along with some load testing to see what the web service can handle.

Zajn
  • 4,078
  • 24
  • 39
  • googling did not help? http://www.codeproject.com/KB/cs/C__Reflection_Tutorial.aspx – Zenwalker Jan 06 '12 at 18:06
  • Are the methods you are trying to call static or instance methods? – Nick Butler Jan 06 '12 at 18:30
  • They're all instance methods that I'm trying to call. I'm beginning to think that I'm going about it wrong, since the class from which I'm getting the methods is the class that _tests_ the class I want to test... so perhaps I should be retrieving the methods from the class that needs tested itself. – Zajn Jan 06 '12 at 18:34

3 Answers3

1

You can use something like this to get the methods you want:

private List<MethodInfo> GetMethods(Type type)
{
        return (from method in type.GetMethods()
                where method.IsPublic &&
                    method.ReturnType == typeof(void)
                select method).ToList();
}

Then if you want to call the methods in separate threads you would write (will work only if the methods are static):

foreach(MethodInfo mi in GetMethods(SomeType) {
   MethodInfo tempMi = mi;
   Action myAction = (Action) Delegate.CreateDelegate(typeof(Action), tempMi);
   ThreadStart ts = new ThreadStart(myAction);
   Thread thread1 = new Thread(ts);
   thread1.Start();
}

Or you would write (only if the methods have strictly no parameters, beware of inherited methods which may take parameters!):

foreach (MethodInfo mi in GetMethods(type))
{
   MethodInfo tempMi = mi; //modified closure
   object o = Activator.CreateInstance( type );
   Action myAction = delegate() { tempMi.Invoke(o, null); };
   ThreadStart ts = new ThreadStart(myAction);
   Thread thread1 = new Thread(ts);
   thread1.Start();
}

If the methods take parameters, you'd have to pass an array of object( object [] { ... } ) instead of null (in the Invoke method called on the current MethodInfo) accordingly; with of course corrects objects in the array.

It would actually be better if you take a List of Thread and add a new Thread in it for each MethodInfo in the list so you can keep control on them afterwards (like if you want to stop one). A HashMap would also be a good choice, the key being the MethodInfo or the Action and the value being the associated Thread.

Max
  • 3,453
  • 3
  • 32
  • 50
  • This looks promising, but I've altered my code as such and I get an error "error binding to target method" at run-time. This may be an error in my code elsewhere and not with your logic. – Zajn Jan 06 '12 at 18:38
  • 1
    You've assumed the methods are static. – Nick Butler Jan 06 '12 at 18:44
  • Maybe that's because your methods being tested have arguments while they should not if you use this piece of code since we are using the Action type here. Edit @NicholasButler is right. – Max Jan 06 '12 at 18:48
  • I have added a piece of code to my answer thanks to @NicholasButler. – Max Jan 06 '12 at 19:12
  • 1
    You've got a [modified closure](http://stackoverflow.com/questions/304258/access-to-modified-closure-2) in the delegate. You need to add a temp `mi` inside the loop. – Nick Butler Jan 06 '12 at 19:24
0

You can implement a private dictionary which holds the MethodInfos and Method name.

private Dictionary<string, MethodInfo> methodList;
        private List<string> GetMethods(Type type)
        {
            methodList = new Dictionary<string, MethodInfo>();

            type.GetMethods().Where(m=>m.IsPublic && m.ReturnType.Equals(typeof(void))).ToList().ForEach(m=>
                methodList.Add(m.Name,m)
                );

            return methodList.Keys.Select(k => k).ToList();
        }

on selecting the dropdown, you can find the method in the dictionary and execute it.

Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
Manas
  • 2,534
  • 20
  • 21
0

You can create an instance of your class using Activator.

Then you can call one of its methods using Invoke.

Something like this should work:

private void RunMethods(Type type)
{
    foreach( var item in lstMethodList.SelectedItems )
    {
        foreach( var method in type.GetMethods() )
        {
            if( String.Equals( item.ToString(), method.Name))
            {
                MethodInfo capturedMethod = method;
                var t = new Thread( () => ThreadMain( type, capturedMethod ) );
                t.Start();
            }
         }
     }
}

static void ThreadMain( Type type, MethodInfo mi )
{
    object o = Activator.CreateInstance( type );

    object[] parameters = new object[] { };

    mi.Invoke( o, parameters );
}

I've assumed the class being tested has a parameter-less constructor.

Nick Butler
  • 24,045
  • 4
  • 49
  • 70
  • Yes, the class being tested has a parameter-less constructor. Though my class set up is a bit convoluted now that I think of it. I have a class that has unit tests for testing that class that I want to test... and I'm trying to call that unit test class from this Windows Form. I don't want to do that in the final product, but I'm doing so right now to test that I can actually _call_ methods from the Windows Form. I think I may need another class that has integration tests rather than unit tests. – Zajn Jan 06 '12 at 18:41
  • I just tried this now and I think it's got me on the right track. Thanks Nicholas! – Zajn Jan 06 '12 at 20:47