Main Site Documentation

Very basic unit test framework


#1

Threw this together really quick. Not very optimized but it does the basics. I sure do miss Linq, Attributes, PropertyInfo, IList, extensions, etc… ;D

I have my sln divided up into 2 micro console applications (my application and a unit test project) and a common library. Test classes (in your test app) must implement ITestClass interface, and test methods must be prefixed with “Test” and return a bool.

I hope this helps someone!


public interface ITestClass
    {
    }
public class Test
    {
        public string Class { get; set; }
        public Type ClassType { get; set; }
        public MethodInfo[] MethodTypes { get; set; }
    }
private static void runTests()
        {
            ArrayList tests = getTestList();

            int testCounter = 0;
            int testFailCounter = 0;
            foreach (Test test in tests)
            {
                var ctor = test.ClassType.GetConstructor(new Type[0]);
                var classInstance = ctor.Invoke(new object[0]);

                foreach (MethodInfo method in test.MethodTypes)
                {
                    testCounter++;
                    Debug.Print("Running " + test.Class + "::" + method.Name);
                    bool returnValue = (bool)method.Invoke(classInstance, new object[0]);
                    if (!returnValue) testFailCounter++;
                    Debug.Assert(returnValue, "Test failed " + test.Class + "::" + method.Name);
                }
            }
            Debug.Print("Ran " + testCounter.ToString() + " tests");
            Debug.Print(testFailCounter + " tests failed");
        }

        private static ArrayList getTestList()
        {
            const string TEST_METHOD_PREFIX = "Test";
            ArrayList tests = new ArrayList();

            Assembly thisAssembly = Assembly.GetAssembly(typeof(Program));

            foreach (Type classType in thisAssembly.GetTypes())
            {
                foreach (Type interfaceType in classType.GetInterfaces())
                {
                    if (typeof(ITestClass) == interfaceType)
                    {
                        ArrayList methodInfos = new ArrayList();
                        foreach (MethodInfo method in classType.GetMethods())
                        {
                            if (method.Name.Substring(0, TEST_METHOD_PREFIX.Length) == TEST_METHOD_PREFIX)
                            {
                                methodInfos.Add(method);
                            }
                        }
                        tests.Add(new Test() { Class = classType.Name, ClassType = classType, MethodTypes = (MethodInfo[])methodInfos.ToArray(typeof(MethodInfo)) });
                    }
                }
            }
            return tests;
        }



#2

Thanks. I bet you are a C# professional


#3

Hey Gus,

How about an area to post .Net Micro C# Q&A’s along with code snippets?


#4

no need, we have a whole thing that we are developing just for what you want to do. Should be ready in a week


#5

Thanks Quince for this post!

This code is very smart, and makes a great use of reflection.
I can’t belive this works on micro framework! amazing!

Maybe you could remove the necessity to inherite from a ITest interface. You could maybe search for classes with a specific prefix, like you did with the “Test” prefix for methods.

MattJack


#6

MattJack,

Yeah the inconsistency with the interface is sloppy. I originally created this using attributes and discovered that they are not available in system.reflection using the micro framework. >:(

It also makes a poor assumption that the assembly is “Program”.

Here you go!


public class Test
    {
        public string Class { get; set; }
        public Type ClassType { get; set; }
        public MethodInfo[] MethodTypes { get; set; }
    }

public class Program
    {
        /// <summary>
        /// Test classes must be prefixed with "TestCase".
        /// Test methods must be prefixed with "Test".
        /// </summary>
        public static void Main()
        {

            Assembly thisAssembly = Assembly.GetAssembly(typeof(Program));
            runTests(thisAssembly);

        }
}


private static void runTests(Assembly testAssembly)
        {
            Test[] tests = getTestList(testAssembly);

            int testCounter = 0;
            int testFailCounter = 0;

            long startTime = DateTime.Now.Ticks;
            
            foreach (Test test in tests)
            {
                var ctor = test.ClassType.GetConstructor(new Type[0]);
                var classInstance = ctor.Invoke(new object[0]);

                foreach (MethodInfo method in test.MethodTypes)
                {
                    testCounter++;
                    Debug.Print("Running " + test.Class + "::" + method.Name);
                    bool returnValue = (bool)method.Invoke(classInstance, new object[0]);
                    if (!returnValue) testFailCounter++;
                    Debug.Assert(returnValue, "Test failed " + test.Class + "::" + method.Name);
                }
            }

            long endTime = DateTime.Now.Ticks;

            TimeSpan span = new TimeSpan(endTime - startTime);

            Debug.Print("Ran " + testCounter.ToString() + " tests in " + span.Milliseconds.ToString() + " milliseconds");
            Debug.Print(testFailCounter + " tests failed");
        }

        private static Test[] getTestList(Assembly testAssembly)
        {
            const string TEST_CLASS_PREFIX = "TestCase";
            const string TEST_METHOD_PREFIX = "Test";

            ArrayList tests = new ArrayList();
            
            foreach (Type classType in testAssembly.GetTypes())
            {
                if (classType.Name.Length > TEST_CLASS_PREFIX.Length 
                    && classType.Name.Substring(0, TEST_CLASS_PREFIX.Length) == TEST_CLASS_PREFIX)
                {
                    ArrayList methodInfos = new ArrayList();
                        foreach (MethodInfo method in classType.GetMethods())
                        {
                            if (method.Name.Length > TEST_METHOD_PREFIX.Length 
                                && method.Name.Substring(0, TEST_METHOD_PREFIX.Length) == TEST_METHOD_PREFIX)
                            {
                                methodInfos.Add(method);
                            }
                        }
                        tests.Add(new Test() 
                            { 
                                Class = classType.Name, 
                                ClassType = classType, 
                                MethodTypes = (MethodInfo[])methodInfos.ToArray(typeof(MethodInfo)),
                            });
                }
            }
            return (Test[]) tests.ToArray(typeof(Test));
        }




#7

Thanks Quince.