-1

所以这是一个示例单例:

public class Singleton
{
    private static Singleton _instance = new Singleton();

    private Singleton()
    {
        //do stuff
    }

    public static Singleton Get()
    {
        return _instance;
    }
}

我正在尝试在这个单例类上运行多个测试,第一个自然而然地总是有效。基本上,我可以看到当我创建一个new Singleton()构造函数时被调用。或者,当我调用Singleton.Get()构造函数时。 测试单独工作正常,但是静态字段已设置,即使我手动将其设置为 null,它仍然不会为下一次测试自行重置。这对我来说是个问题,因为自动化测试在这里很重要。那么我怎样才能让它自己重置呢?

到目前为止,我研究了 AppDomain 并发现我可以将每个测试放入自己的程序集中并手动运行它。但是,当然,VS已经为每个程序集使用了一个唯一的 AppDomain,这将允许自动发生这种情况,但这是很多测试程序集,看起来有点傻。我正在寻找替代方案。

注意:我不想要关于代码质量的建议。 这是我正在测试的代码,而不是我编写的代码。在我先测试之前,我不会更改代码。 我对此进行了彻底的研究,并没有找到一个没有用“你不应该使用单例”来回答的问题。我对代码建议不感兴趣。这是 StackOverflow,而不是 CodeReview。

要求提供额外信息

我目前正在使用 Visual Studio 的内置测试框架运行测试。我希望它在移至 MSBuild 时仍然有效。这并不排除手动触发测试的外部应用程序,但它确实使它更难做到。

4

5 回答 5

2

您误解了 AppDomains。

您可以简单地为每个测试创建一个新的 AppDomain,将所有测试程序集加载到其中,并在每个域中仅调用一个测试方法。
不过,它可能会相当慢。

于 2013-04-17T19:49:59.847 回答
1

您可以Singleton像这样设计您的课程:

public class Singleton
{
    private static Singleton _instance;
    private static object _instanceLock = new object();

    private Singleton()
    {
        //do stuff
    }

    public static Singleton Get()
    {
        if (_instance == null)
        {
            lock(_instanceLock)
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
            }
        }

        return _instance;
    }

    public static void Clear()
    {
        if (_instance != null)
        {
            lock(_instanceLock)
            {
                if (_instance != null)
                {
                    _instance = null;
                }
            }
        }
    }
}

然后你必须Singleton.Clear()在开始每个测试之前打电话,像这样:

[TestInitialize]
public void Initialize()
{
    Singleton.Clear();
}
于 2013-04-17T19:49:59.493 回答
1

编写一个带有命令行参数的控制台程序,以确定要运行哪些单例测试。然后从批处理文件(或 bash 或 powershell,无论您喜欢哪个)中多次调用它。这是额外的工作,但它可以让您每次都在全新的环境中测试此代码。或者您可以尝试弄清楚您当前的工具中是否有一些选项可以做到这一点。

也许是这样的:

static int Main(string[] args) {
   try {
      int testcase = (Int32.Parse(args[0]));
      RunTest(testcase);
   } catch (Exception x) {
      Console.WriteLine("test failed: "+x.Message);
      return 1;
   }
   Console.WriteLine("test passed.");
   return 0;
}
于 2013-04-17T20:25:16.873 回答
1

经过@redtuna @drch 和@SLaks 的大量建议以及大量谷歌搜索,我们确定了一种方法来做到这一点。

第 1 步:您的测试类需要继承自MarshalByRefObject

public class UnitTest1 : MarshalByRefObject

第 2 步:下一部分是一个自定义方法,用于在它自己的AppDomain.

private void RunTestInCustomDomain(string methodName)
{
    // selecting the dll from the debug directory using relative directories
    var testDll = @"..\..\..\UnitTests\bin\Debug\UnitTests.dll";
    // first verify the dll exists
    Assert.IsTrue(File.Exists(testDll));
    var assemblyName = AssemblyName.GetAssemblyName(testDll).FullName;
    var domain = AppDomain.CreateDomain(methodName, null, new AppDomainSetup()
    {
        // This is important, you need the debug directory as your application base
        ApplicationBase = Path.GetDirectoryName(testDll)
    });
    // create an instance of your test class
    var tests = domain.CreateInstanceAndUnwrap(assemblyName, typeof(UnitTest1).FullName) as UnitTest1;
    var type = tests.GetType();
    var method = type.GetMethod(methodName);
    // invoke the method inside custom AppDomain
    method.Invoke(tests, new object[]{});
    // Unload the Domain
    AppDomain.Unload(domain);
}

第 3 步:下一个技巧是您的测试方法正在调用此自定义方法,因此您的测试将写入没有该属性的公共方法中。[TestMethod]

[TestMethod]
[DeploymentItem("UnitTests.dll")]
public void TestMethod1()
{
    RunTestInCustomDomain("actual_TestMethod1");
}

public void actual_TestMethod1()
{
    // Assert Stuff
}

为了完整性: 如果您需要为每个测试运行初始化或清理,则需要手动调用它们,因为 TestMethod 与实际_TestMethod 在不同的 AppDomain 中运行

public void actual_TestMethod1()
{
    // Assert Stuff
    TestCleanup();
}

编辑:我应该注意,因为这些方法是在单独的 AppDomain 下运行的,所以代码覆盖率不起作用:(。如果有人找到除此功能之外的方法来获得该功能,请告诉我。

于 2013-04-18T15:41:28.303 回答
0

只是一个想法:

当然,将其设置为 null 只是将您的变量设置为 null 而不是它所指向的内容。但是如果你能得到它Singleton自己呢......好吧,似乎这可以使用Expressions 来完成。请参阅从 MemberExpression 中获取对象?

于 2013-04-17T20:49:05.897 回答