4

Similar to this for Java: How can I make my JUnit tests run in random order?

And opposite to this for .Net: http://blogs.msdn.com/b/slange/archive/2010/06/02/ordering-method-execution-of-a-coded-ui-test.aspx

My reason is the same as in the first link - I wish to uncover any dependencies between the tests by shuffling the order every time they run.

Can this be done? I have to use MSTest because my tests drive GUI - they use Coded UI.

Community
  • 1
  • 1
Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147

2 Answers2

3

Since this hasn't been answered, I'll take a stab at it.

I don't know if the testing framework has this built in, but I would just code it myself. The simplest way might be to write a test method that calls all of the other test methods in a random order. You can be completely creative in the way you do this, you'll just want to set Playback.PlaybackSettings.ContinueOnError to true so the overall test doesn't fail when a single test fails.

mejdev
  • 3,509
  • 4
  • 24
  • 38
  • Good one. A little note though. If you call the Test Methods by code, the `TestMethod` attribute is ignored and the Initialize/Cleanup methods are not called. So, in case they are all the same you put them in the base test method, or you have to call them before/after each test method. – chaliasos Aug 11 '12 at 09:14
  • You will also loose individual results if that is important to you. Probably only matters if you are hooking them into test manager and looking at the stats for test passes and fails from there. – stoj Aug 13 '12 at 01:26
  • In that case though, it isn't too hard to simply write your own test results. You just have to worry about consistency a little bit more. – mejdev Aug 13 '12 at 12:45
1

I needed something similar so this may give you or anyone else a head start. You can just drop this in as it's own test class and it will automatically run every other unit test method in the same assembly a single time in a random order spanning unit test classes.

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Company.UnitTests
{
    [TestClass]
    public class RandomizerTest
    {
        private class TestClassProxy
        {
            public object Instance { get; set; }
            public Type Type { get; set; }
            public Action ClassCleanup { get; set; }
            public Action<TestContext> ClassInitialize { get; set; }
            public Action TestCleanup { get; set; }
            public Action TestInitialize { get; set; }
            public List<Action> TestMethods { get; set; }
        }

        [TestMethod]
        public void TestRandom()
        {
            // ARRANGE

            // attributes we'll be using to build our proxies (change these for NInject, other UT frameworks)
            var classInitializeAttributeType = typeof (ClassInitializeAttribute);
            var classCleanupAttributeType = typeof (ClassCleanupAttribute);
            var testInitializeAttributeType = typeof (TestInitializeAttribute);
            var testCleanupAttributeType = typeof (TestCleanupAttribute);
            var testMethodAttributeType = typeof (TestMethodAttribute);

            var proxies = (
                              from type in Assembly.GetExecutingAssembly().GetTypes()
                              where
                                  type != typeof (RandomizerTest) && // don't include this class (infinite-loop)
                                  type.GetCustomAttributes(typeof (TestClassAttribute), false).Length > 0 // only classes attributed with [TestClass]
                              let methods = type.GetMethods() // keep the methods for re-use
                              let instance = Activator.CreateInstance(type)
                              select new TestClassProxy
                              {
                                  Type = type,
                                  Instance = instance,
                                  ClassInitialize = // select and wrap the method invokes inside an Action for re-use
                                      methods
                                          .Where(λ =>
                                                 λ.GetCustomAttributes(classInitializeAttributeType, false).Any())
                                          .Select(λ => (Action<TestContext>) (tc => λ.Invoke(instance, new object[] { tc })))
                                          .FirstOrDefault() ?? delegate { },
                                  ClassCleanup =
                                      methods
                                          .Where(λ =>
                                                 λ.GetCustomAttributes(classCleanupAttributeType, false).Any())
                                          .Select(λ => (Action) (() => λ.Invoke(instance, null)))
                                          .FirstOrDefault() ?? delegate { },
                                  TestInitialize =
                                      methods
                                          .Where(λ =>
                                                 λ.GetCustomAttributes(testInitializeAttributeType, false).Any())
                                          .Select(λ => (Action) (() => λ.Invoke(instance, null)))
                                          .FirstOrDefault() ?? delegate { },
                                  TestCleanup =
                                      methods
                                          .Where(λ =>
                                                 λ.GetCustomAttributes(testCleanupAttributeType, false).Any())
                                          .Select(λ => (Action) (() => λ.Invoke(instance, null)))
                                          .FirstOrDefault() ?? delegate { },
                                  TestMethods =
                                      methods
                                      .Where(λ =>
                                             λ.GetCustomAttributes(testMethodAttributeType, false).Any())
                                      .Select(λ => (Action) (() => λ.Invoke(instance, null))).ToList(),
                              }).ToList();

            var random = new Random();

            // ACT

            // Note that the following may not work depending on how you developed your unit tests. 
            // If they're sharing state in any way (SQL DB, etc.) this may not be what you want.
            // If that's the case alter the code below to only randomly sample test methods inside each class 
            //   so that you can isolate tests per test class.
            // This methodology assumes the cardinal rule: All unit tests are atomic. (given their proper setup/teardown)
            // Plus if you're testing in a random order this is likely what you're after anyway.

            // initialize all classes
            foreach (var testClassProxy in proxies)
            {
                testClassProxy.ClassInitialize(null);
            }

            // run all test methods in a random order spanning all test classes until we run out 
            while (proxies.Count > 0)
            {
                // get random test class proxy
                var proxy = proxies[random.Next(0, proxies.Count)];

                // get random test method from proxy
                var testMethod = proxy.TestMethods[random.Next(0, proxy.TestMethods.Count)];

                // run test initialize
                proxy.TestInitialize();

                // run test method
                testMethod(); // (ASSERT)

                // run test cleanup
                proxy.TestCleanup();

                // remove test method from processing
                proxy.TestMethods.Remove(testMethod);

                // still have methods?
                if (proxy.TestMethods.Count > 0)
                {
                    continue;
                }

                // no, run class cleanup routine
                proxy.ClassCleanup();

                // remove the proxy from processing
                proxies.Remove(proxy);
            }
        }
    }
}
Dave Jellison
  • 924
  • 13
  • 22
  • Looks pretty cool, but how did you get the `lambda` symbol to not throw off a compiler? – Hamish Grubijan Oct 05 '12 at 20:08
  • You can use unicode inside Visual Studio, just do a find/replace if needed sorry. The lambda is shorthand syntax technique i use in code for the lorem ipsum effect to let the eyes focus on the more interesting parts of the code. – Dave Jellison Oct 05 '12 at 20:10
  • I did not know about Unicode identifiers; that is very cool, but boy can I think of all kinds of malicious code that will pass a code review ;) – Hamish Grubijan Oct 05 '12 at 20:21
  • lol - lemme know if it works out for you or accept the answer. Obviously you can just iterate this proccess to do a more intense random analysis (a one shot random-through isn't that great) – Dave Jellison Oct 05 '12 at 20:23
  • 1
    Dave, I asked this question over a year ago so I moved on to another project and I will be leaving (at least for some time) .Net & Windows behind very soon, so I probably will not be able to check out this approach in action, but it does look pretty good at the first glance and I hope that this will be of help to someone else. Once this answer gets enough votes, and someone leaves a comment saying this this answer should be the accepted one, I will change the question's status. Once again, thank you for contributing. – Hamish Grubijan Oct 05 '12 at 23:13