VS2008单元测试 - 断言方法退出

5
我正在尝试使用VS 2008内置的单元测试框架编写C#单元测试,而我要测试的方法调用了Environment.Exit(0)。当我在单元测试中调用此方法时,我的单元测试会被中止。该方法确实应该调用Exit,我想找到一种方法来测试它是否这样做,并测试它使用的退出代码。我应该怎么做?我查看了Microsoft.VisualStudio.TestTools.UnitTesting Namespace,但没有看到任何相关内容。
[TestMethod]
[DeploymentItem("myprog.exe")]
public void MyProgTest()
{
    // Want to ensure this Exit's with code 0:
    MyProg_Accessor.myMethod();
}

同时,这是我想要测试的代码要点:
static void myMethod()
{
    Environment.Exit(0);
}

编辑:这是我在测试方法中使用的解决方案,感谢RichardOD提供的帮助:

Process proc;

try
{
    proc = Process.Start(path, myArgs);
}
catch (System.ComponentModel.Win32Exception ex)
{
    proc = null;
    Assert.Fail(ex.Message);
}

Assert.IsNotNull(proc);
proc.WaitForExit(10000);
Assert.IsTrue(proc.HasExited);
Assert.AreEqual(code, proc.ExitCode);
6个回答

5

您需要为Environment类创建一个包装器,然后在代码中使用该包装器。对于单元测试,注入包装器的模拟版本。以下示例使用RhinoMocks验证方法是否将预期参数传递给包装器。

public class EnvironmentWrapper
{
    public virtual void Exit( int code )
    {
        Environment.Exit( code );
    }
}


public class MyClass
{
    private EnvironmentWrapper Environment { get; set; }

    public MyClass() : this( null ) { }

    public MyClass( EnvironmentWrapper wrapper )
    {
        this.Environment = wrapper ?? new EnvironmentWrapper();
    }

    public void MyMethod( int code )
    {
        this.Environment.Exit( code )
    }
}


[TestMethod]
public void MyMethodTest()
{
     var mockWrapper = MockRepository.GenerateMock<EnvironmentWrapper>();

     int expectedCode = 5;

     mockWrapper.Expect( m => m.Exit( expectedCode ) );

     var myClass = new MyClass( mockWrapper );

     myclass.MyMethod( expectedCode );

     mockWrapper.VerifyAllExpectations()
}

1
不错的例子,如果我没有在吃晚餐的话,我也会写类似的代码!你的代码让我想起了ASP.NET MVC项目中AccountController类的默认代码——这只能是一件好事。个人认为,我会将public EnvironmentWrapper Environment { get; set; }改为private set,但除此之外,很棒的例子。 - RichardOD

4
这听起来像是一个非常糟糕的主意。Environment.Exit(0)会按照指示执行,因此你的单元测试会出问题。
如果你真的想测试这个,你可以启动一个独立的进程并检查返回代码-看看是否可以用Process.Start封装它。
我想另一个选择是将这段代码拆分出来并注入test spy,或者使用模拟对象来验证正确的行为。
也许你可以使用Typemock Isolator-我相信它可以模拟静态方法

在这里给TypeMock Isolator点个赞 - 这是我所知道的唯一可以拦截和模拟任何东西的解决方案。 - Pavel Minaev
能够模拟静态方法的危险在于你不容易戒掉使用它们的习惯。对于我的代码而言,我发现被迫更加努力地编写静态方法是一件好事,因为除非它们是绝对最佳解决方案,否则我不会使用它们。在我看来,这有助于强制采用更好的技术进行开发。缺点是当与那些确实有意义的静态方法交互时(或者框架没有考虑测试),你被迫跳过许多障碍。 - tvanfosson
@tvanfosson- 这是一个很好的观点。这就是为什么许多人(包括我自己)在编写可测试代码时尽量避免过度使用静态方法。.NET框架中的静态类很难进行测试,开发人员经常不得不编写包装器代码来使其可测试(根据您的答案)。从ASP.NET Web表单到ASP.NET MVC的演变中,您可以看到这种情况发生了很多次。 - RichardOD

3
您将无法进行测试-Environment.Exit会完全终止应用程序。这意味着使用此代码的任何AppDomain都将完全卸载,无论是您的生产应用程序还是单元测试框架。请注意保留HTML标记。

2
您在这里唯一的选择是使用一个伪造的 Exit 方法模拟 Environment 类。

0

我想到的唯一一件事情是:

static void myMethod()
{
    DoEnvironmentExit(0);
}

static void DoEnvironentExit(int code)
{
    #if defined TEST_SOLUTION
      SomeMockingFunction(code);
    #else
      Environment.Exit(code);
    #endif
}

0

你可以在方法中添加一个参数,以便传递一个虚拟环境,其中exit()方法不会退出。

你可以从应用程序调用的方法中提取这个参数化方法,并对提取的函数进行单元测试。这样,你就不必修改你的应用程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接