有没有办法确定它是否是测试(.NET)?

时间:2009-10-14 19:43:13

标签: visual-studio unit-testing exception-handling mstest

我希望我的应用程序捕获异常并在生产中运行时通过电子邮件发送它们,但是当我运行MSTest单元测试时,我希望它抛出异常,所以我可以调试。我当前的代码总是通过电子邮件发送异常。有什么方法可以知道单元测试是否调用了当前进程?

我能想到的一种方法是测试Enviroment.CurrentDirectory和类似的变量。还有一个更好的吗?

7 个答案:

答案 0 :(得分:3)

您还可以在App.Config上设置一个标志,以便电子邮件例程验证是否应该发送这些电子邮件,并在Test Suite中将该标志设置为false。

答案 1 :(得分:2)

这是依赖注入或服务定位器的经典用例。您可以从服务定位器获取通知服务,然后让它调用,而不是将应用程序硬连接到发送电子邮件。在生产中,配置服务定位器以返回电子邮件发送服务;在测试环境中,将其配置为返回不执行任何操作的服务,或将通知添加到列表或其他任何内容。

你不需要在这里进行完整的whack依赖注入:一个非常简单的服务定位器就足够了。类似地,可以通过测试夹具代码完成测试通知服务的注入。这是一个非常简单的例子:

public static class ServiceLocator
{
  private static INotificationService _notificationService = new EmailNotificationService();

  public static INotificationService NotificationService
  {
    get { return _notificationService; }
  }

  // For test use only
  public static void SetNotificationService(INotificationService notificationService)
  {
    _notificationService = notificationService;
  }
}

(一个真正的服务定位器将提供一个更灵活的接口,沿着IServiceContainer / IServiceProvider,你可以模拟多个不同的服务;这只是为了说明这个想法。)

然后在你的测试代码中:

[SetUp]
public void Setup()
{
  ServiceLocator.NotificationService = new DiscardingService();
}

(在这里使用NUnit术语表示在每次测试之前运行的方法 - 不确定MSTest等价物是什么。)

这样做的一个优点是您现在可以测试是否正在发送正确的通知:让您的测试通知服务捕获列表中的通知,并且您可以对该列表进行断言以验证是否正在发送通知正确的。

再次注意,这不是DI或服务定位器的完整解释,我的示例代码绝不是最好的方法。

答案 2 :(得分:1)

您可以使用编译指令(如#IF DEBUG)在测试时执行某些代码段,并在发布模式下编译时使用其他代码集;

另一种方法可以是编写不同的TraceListeners,这样您就可以用纯文本记录异常,或者只需在.config文件中设置即可发送电子邮件。

答案 3 :(得分:1)

你可以做一些真正的事情,比如使用log4net和我们的IsDebug标志。这当然是疯狂的。

更好的方法是注入任何将电子邮件发送到您的类中的内容,当您运行单元测试时,为此传递模拟对象(moq),这样在代码在测试条件下运行时将不会发送电子邮件。 / p>

有几个很好的框架可以做到这一点

起订量 犀牛嘲笑

这是我的两个最爱。

当我谈论注射时我的意思是这样的

public class Foo
{
    private Emailer _emailer;

    public Foo(Emailer mailer)
    {
        _emailer = mailer;
    }

    public void SomeMethod()
    {
         ...
         try
         {
             ...
         }
         catch(SomeException ex)
         {
             _emailer.SendEmail(ex);
         }
         finally
         {}
    }
}

框架本质上允许您在某些条件下传入一个基本上是伪造的对象,然后您可以在nunit中指明这些对象的行为并使用断言等。

但要直接回答你的问题,你可以在配置文件中使用一个标志,但这不是那么整洁或有用。

答案 4 :(得分:0)

嗯,基本上你要做的就是在测试过程中模拟出发送的邮件。 我假设您有一个负责发送电子邮件的类,您的代码类似于:

..
..
catch(Exception e)
{
   EmailSender.Send(e);
}

应该隔离一个好的单元测试,这意味着执行代码不应该跨越类边界,更不用说向其他进程发送消息了!

我建议你阅读网上关于Mocking和Mocking框架的资料。 我个人使用RhinoMock框架。

答案 5 :(得分:0)

如果您更详细地描述了您的具体情况,那将会有所帮助。

如果他们发送电子邮件,这听起来不像是在运行单元测试。代码单元不应该控制,或者确实具有具体错误报告类型的任何知识,除非它们可以在应用程序级别轻松配置。

听起来你要做的就是破解未正确设置的东西。如果问题是非关键问题并且肯定可以继续安全,那么您应该只使用记录器然后适当地响应错误。如果您仍然需要该功能,可能会有一个log4net appender发送电子邮件(如果您不配置log4net,它将不会执行任何操作,因此您的测试不会发送任何电子邮件)。

这不会影响可测试性,因为您可以输入错误条件,然后检查是否采取了适当的响应(例如“如果我无法读取配置文件,则返回默认值”)。

如果您正在检测致命错误,那么您应该在应用程序的顶层(即启动后立即)创建异常处理程序。您可以通过连接AppDomain.UnhandledException事件的处理程序来完成此操作。这样,如果未处理异常,您仍然可以正确捕获它,通过异常处理程序发送电子邮件,然后以优雅的方式终止应用程序。

您只能在正常的应用程序运行开始时安装异常处理程序。保存最初的靴子/布线阶段,您的应用程序不知道它。您的代码单元不会发现,您可以像往常一样进行测试。

答案 6 :(得分:0)

快速的方式可能是......

public static class Tester
{
    public static bool InUnitTest
    {
        get
        {
            return Environment.StackTrace.IndexOf("Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestRunner") > 0;
        }
    }
}

然后在您的应用程序中,您可以

catch (Exception e)
{
    if (!Tester.InUnitTest)
    {
       ... // send mail
    }
}

你也可以在你可能正在使用的任何Emailsender类中实现它,这样你就不必担心改变每个catch块