与 Java 类似: 如何让我的 JUnit 测试以随机顺序运行?
与.Net相反:http: //blogs.msdn.com/b/slange/archive/2010/06/02/ordering-method-execution-of-a-coded-ui-test.aspx
我的理由与第一个链接中的相同 - 我希望通过在每次运行时重新排序来发现测试之间的任何依赖关系。
这可以做到吗?我必须使用MSTest
,因为我的测试驱动 GUI - 他们使用编码的 UI。
与 Java 类似: 如何让我的 JUnit 测试以随机顺序运行?
与.Net相反:http: //blogs.msdn.com/b/slange/archive/2010/06/02/ordering-method-execution-of-a-coded-ui-test.aspx
我的理由与第一个链接中的相同 - 我希望通过在每次运行时重新排序来发现测试之间的任何依赖关系。
这可以做到吗?我必须使用MSTest
,因为我的测试驱动 GUI - 他们使用编码的 UI。
既然这个问题还没有回答,那我就试一试吧。
我不知道测试框架是否内置了这个,但我会自己编写代码。最简单的方法可能是编写一个测试方法,以随机顺序调用所有其他测试方法。您可以完全有创意地执行此操作,您只需将 Playback.PlaybackSettings.ContinueOnError 设置为 true,以便在单个测试失败时整体测试不会失败。
我需要类似的东西,所以这可能会给你或其他任何人一个良好的开端。您可以将其作为它自己的测试类放入,它会以跨越单元测试类的随机顺序自动运行同一程序集中的所有其他单元测试方法。
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);
}
}
}
}