有什么方法可以识别它是否是测试(.NET)?

发布于 2024-08-07 14:21:22 字数 184 浏览 2 评论 0原文

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

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

I want that my application catches the exceptions and e-mail them when running in production, but when I am running my MSTest unit tests I want it to throw the exceptions, so I can debug. My current code always e-mails the exceptions. There is any way to know if the current process was invoked by the unit tests?

One way I can think is to test the Enviroment.CurrentDirectory and similar variables. There is a better one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

北座城市 2024-08-14 14:21:22

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

You can also set a flag on your App.Config for the e-mail routine to verify if it should send those e-mails, and in your Test Suite set that flag to false.

枫以 2024-08-14 14:21:22

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

您不需要在这里进行完整的依赖注入:一个非常简单的服务定位器就足够了。同样,测试通知服务的注入可以通过测试夹具代码来完成。这是一个非常简单的示例:(

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 或服务定位器的完整解释,并且我的示例代码绝不是最好的方法。

This is a classic use case for dependency injection or a service locator. Instead of hardwiring your application to send emails, have your application get a notification service from the service locator, and have it call that. In production, configure the service locator to return an email sending service; in the test environment, configure it to return a service that does nothing, or add the notification to a list, or whatever.

You don't need to go for the full whack dependency injection here: a very simple service locator will suffice. Similarly, the injection of the test notification service can be done through test fixture code. Here's a really simple example:

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;
  }
}

(A real service locator would provide a more flexible interface, along the lines of IServiceContainer/IServiceProvider, so that you could mock out multiple different services; this is just to illustrate the idea.)

Then in your test code:

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

(Using NUnit terminology here for the method that gets run before each test -- not sure what the MSTest equivalent is.)

An advantage of this is that you can now test that the right notifications are being sent: have your test notification service capture the notifications in a list, and you can make assertions about that list to verify that notifications are being sent and are correct.

Again, note that this is not a full explanation of DI or service locators, and my sample code is by no means the best way to do it.

梦明 2024-08-14 14:21:22

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

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

You could use compilation directives (like #IF DEBUG) to execute some code piece when testing and another code set when compiled in released mode;

Another approach can be write different TraceListeners, so you can log your exceptions in plain text or send your email just by setting up in your .config file.

旧城烟雨 2024-08-14 14:21:22

你可以做一些非常邪恶的事情,比如使用 log4net 和 IsDebug 标志。这当然是疯狂的。

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

有几个很好的框架可以完成这个

最小起订量
Rhino Mocks

这是我最喜欢的两个。

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

public class Foo
{
    private Emailer _emailer;

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

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

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

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

You could do something REALLY EVIL like use log4net and us the IsDebug flag . this is of course madness.

A better way would be to Inject whatever sends the email into your class and when you run unit tests pass in a mock object for this (moq) this way the emails will not be sent when code is running under test conditions.

There are several good frameworks for doing this

Moq
Rhino Mocks

these are my two favourites.

When I talk about injection I mean something like this

public class Foo
{
    private Emailer _emailer;

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

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

The frameworks essentially allow you under certain conditions to pass in an object which is basically fake, you can then speicify behaviour of these objects and use assertions etc in nunit.

But to answer you question directly you COULD use a flag in your config file, but this is not as neat or as useful.

忆离笙 2024-08-14 14:21:22

好吧,基本上你想要做的就是在测试期间模拟邮件发送。
我假设您有一个负责发送电子邮件的类,并且您的代码如下所示:

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

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

我建议您阅读网上有关 Mocking 和 Mocking 框架的材料。
我个人使用 RhinoMock 框架。

Well, basically what you want to do is to Mock out the mail sending during testing.
I assume you have a class that is responsible for sending emails and your code looks something like:

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

A good unit test should be isolated, meaning that the executing code should not cross class boundaries, not to mention sending messages to other processes!

I suggest you read up material on Mocking and Mocking frameworks on the net.
I personally use the RhinoMock framework.

羁绊已千年 2024-08-14 14:21:22

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

如果他们发送电子邮件,听起来您并没有在运行单元测试。代码单元不应该进行控制,或者实际上不应该了解具体的错误报告类型,除非它们可以在应用程序级别轻松配置。

听起来您想要做的就是破解一些未正确设置的内容。如果问题不严重并且绝对可以安全地继续,那么您可能应该只使用记录器,然后适当地响应错误。如果您仍然需要该功能,可能有一个 log4net 附加程序可以发送电子邮件(如果您不配置 log4net,它不会执行任何操作,因此您的测试不会发送任何电子邮件)。

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

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

您只需在正常应用程序运行开始时安装异常处理程序。除了初始引导/连接阶段外,您的应用程序对此一无所知。您的代码单元将不知道,并且您可以像往常一样进行测试。

It would help if you described more about your specific scenario.

It doesn't sound like you're running unit tests if they're sending emails. The units of code shouldn't be controlling, or indeed have any knowledge of concrete error reporting types unless they're easily configurable at an app level.

It sounds like what you're trying to do is hack around something that is not set up properly. If the problem is non-critical and it's definitely safe to continue, then you should probably just use a logger and then respond to the error appropriately. There's probably a log4net appender that sends emails if you still need that functionality (and if you don't configure log4net, it won't do anything, hence your tests won't send any emails).

This doesn't impact testability, as you can feed in the error condition and then check whether the appropriate response was taken (e.g. "if I cannot read a config file, then return the defaults").

If you're detecting fatal errors, then you should look at creating an exception handler at the top level of your app (i.e. immediately after it starts). You can do this by wiring up a handler for the AppDomain.UnhandledException event amongst other things. That way, if an exception is unhandled, you will still be able to catch it properly, send the email via your exception handler and then terminate the application in a graceful fashion.

You would only install the exception handler in at the start of a normal application run. Save for the initial bootsrapping/wiring phase, your app has no knowledge of it. Your units of code would be unaware, and you could test like usual.

情徒 2024-08-14 14:21:22

一种快速的方法可能是...

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 块

A quick way could be...

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

Then in your application you can just

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

You could also implement that within any Emailsender class you may be using so you dont need to worry about changing every catch block

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文