如何编写运行时程序集位置的测试用例

时间:2015-06-23 10:41:21

标签: c# unit-testing

这是我创建新应用程序实例的类:

public interface IApplicationInstanceProvider
{
    bool CreateNewProcess();
}

public class ApplicationInstanceProvider : IApplicationInstanceProvider
{
    public bool CreateNewProcess()
    {
        Process p = new Process();
        p.StartInfo.FileName = System.Reflection.Assembly.GetEntryAssembly().Location;
        return p.Start();
    }
}

这是我的测试用例:

[TestMethod]
public void TestMethodForAppInstance()
{
    IApplicationInstanceProvider provider = new ApplicationInstanceProvider();
    bool isCreated = provider.CreateNewProcess();

    Assert.AreEqual(isCreated,true);
}

问题是:System.Reflection.Assembly.GetEntryAssembly()在测试用例执行时为空。但它在应用程序运行时工作正常。 请帮忙!

1 个答案:

答案 0 :(得分:1)

您无法按原样测试您的课程,因为您在测试环境中执行 unknown 新进程。它不可行,因为:

  1. Assembly.GetEntryAssembly()将返回null
  2. 无论它返回什么,你都会执行那个流程,它可能就是devenv.exechrome.exe或其他什么。
  3. CreateNewProcess()方法做了很多事情:它确定执行路径的程序并运行它。此外,其返回值告诉来电者是否已启动流程或现有流程已被重复使用。单个方法太多东西使得测试变得困难。幸运的是,至少有两种方法可以使您的代码可测试:创建一个专门的ApplicationInstanceProvider类来进行测试或为它创建一个单独的类。

    让我们看看第一种方法:

    public class ApplicationInstanceProvider : IApplicationInstanceProvider {
        public bool CreateNewProcess() {
            Process process = new Process();
            process.StartInfo.FileName = ResolveApplicationPath();
            return process.Start();
        }
    
        protected virtual string ResolveApplicationPath() {
            return System.Reflection.Assembly.GetEntryAssembly().Location;
        }
    }
    

    您将创建一个用于测试的派生类:

    sealed class TestApplicationInstanceProvider : ApplicationInstanceProvider {
        protected override string ResolveApplicationPath() {
            // path to your assembly or a well-known executable executable
            // Like %WINDIR%\notepad.exe
            return "...";
        }
    }
    

    然后在你的测试方法中使用它:

    [TestMethod]
    public void TestMethodForAppInstance() {
        var provider = new TestApplicationInstanceProvider();
        bool isCreated = provider.CreateNewProcess();
    
        Assert.AreEqual(isCreated, true);
    }
    

    请注意,您无法测试Assembly.GetEntryAssembly(),但您可以测试所有内容。请注意,现在您正在测试是否创建了新的流程实例,但是您没有检查是否已启动正确一个;这将增加代码覆盖率,但实际上几乎没有测试任何内容因为Process.Start()将始终为可执行文件返回true(运行进程可能重用文档)。这就是为什么你必须分开CreateNewProcess()责任(不仅为了清晰,而且为了测试)。测试后不要忘记关闭清理方法中的流程实例!

    让我们看看第二种方法:第二种方法稍微复杂一点,但它更通用:

    public interface IAssemblyResolver {
        string GetEntryAssemblyPath();
    }
    
    public sealed class DefaultAssemblyResolver : IAssemblyResolver {
        public string GetEntryAssemblyPath() {
            return System.Reflection.Assembly.GetEntryAssembly().Location;
        }
    }
    
    public class ApplicationInstanceProvider : IApplicationInstanceProvider {
        public ApplicationInstanceProvider(IAssemblyResolver resolver) {
            _resolver = resolver;
        }
    
        public bool CreateNewProcess() {
            Process process = new Process();
            process.StartInfo.FileName = _resolver.GetEntryAssemblyPath();
            return process.Start();
        }
    
        private readonly IAssemblyResolver _resolver;
    }
    

    现在你必须创建一个模拟测试:

    sealed class TestAssemblyResolver : IAssemblyResolver {
        public string GetEntryAssemblyPath() {
            // Return path of a well-known test application,
            // for example an "empty" console application. You can also
            // reuse it to, for example, return different error codes
            return Assembly.Load(...);
        }
    }
    

    测试方法:

    [TestMethod]
    public void TestMethodForAppInstance() {
        var resolver = new TestAssemblyResolver();
        var provider = new ApplicationInstanceProvider(resolver);
        bool isCreated = provider.CreateNewProcess();
    
        Assert.AreEqual(isCreated, true);
    }
    

    您的应用程序可能是什么样的?

    static class Program {
        static int Main(string[] args) {
            if (args.Length == 0)
                return 0;
    
            return Int32.Parse(args[0]);
        }
    }