3

我们正在为我们的应用程序的日志记录任务(.NET 3.5)使用内部简单的 Logger 类。

记录器代码很旧,设计类似于:

public class Logger : ILogger
{
     private ILogger instance;

     private static ILogger Instance
     { 
         // Initialized on first use.
         get { return instance; }
     }

     public static void Debug(string msg)
     {
           instance.Debug(msg);
     }

     public static void Error(string msg)
     {
           ....
     }
}

实例本身在第一次使用时被初始化(懒惰地)。

根据其严格的“书本”实现,这不是一个 Singleton,但尽管如此,从所有调用代码对此类的访问都是静态访问

出于测试目的和其他架构原因,我希望能够用其他东西替换内部实例(注入它)。

我怎样才能轻松实现这一目标?我们目前没有使用任何 IoC 容器,但我不想将设置器暴露给 Instance 属性,因为这会破坏整个单例式设计。

关于如何为此提出解决方案的任何建议?

4

5 回答 5

2

考虑使用Fakes Framework进行测试。您可以使用类似这样的方法来存根对静态方法的调用

ShimLogger.Instance = () => new LoggerMock();

在 .net 3.5 的情况下,您可以使用Moles 框架来存根静态方法调用。配置代码将类似于:

MLogger.Instance = () => new LoggerMock();

它需要将静态方法Instance公开,但在此配置之后,每次调用静态方法都会返回您的模拟实例。

于 2012-08-21T19:23:12.250 回答
1

确实,二传手听起来不是一个好的选择。

相反,我会考虑两种可能的方法。一、显式配置方法:

public class Logger : ILogger {

  public void ConfigureLogger( ILogger logger ) {
     this.instance = logger;
  }

}

这种方法的一个优点是意图很明确,而且您必须以显式方式调用此方法。

另一种选择是允许在配置中传递一种类型的记录器:

<appSettings>
    <add key="loggerType" value="The.Type.From, Some.Assembly" />
</appSettings>

然后,在您的Logger类中重写初始化例程,以便如果存在配置参数,您更喜欢配置中提供的类型而不是默认类型。

这种方法的一个优点是您可以使用配置更改重新配置客户端,而无需更改代码。

无论如何,IoC 容器不会咬人。介绍一个,因为它会在长期内得到回报。

于 2012-08-21T19:22:49.833 回答
0

另一个想法是internal为您的属性创建一个设置器Instance并使用InternalsVisibleTo属性使内部设置器对您的测试程序集可见。请注意,如果包含您的记录器的程序集是强命名的,那么您必须PublicKeyInternalsVisibleTo属性中指定 。显然,如果您的记录器位于自己的程序集或大多数开发/日志记录未发生的某种基础设施程序集中,这将是最有帮助的(在不让其他开发人员意外或故意将 Instance 设置为其他东西的意义上) .

于 2012-08-21T19:38:10.683 回答
0

我会使用 Logger 修改代码。不要通过 Logger.Instance 访问记录器,而是将所需的记录器实例传递到对象中。然后在您的工厂和/或组合根中,您将 Logger.Instance 作为生产代码中记录器的源传递,并且在您的单元测试中使用模拟记录器很容易。

public class Foo
{
  private readonly ILogger logger;

  public Foo(ILogger logger)
  {
    if (logger == null)
      throw new ArgumentNullException("logger");
    this.logger = logger;
  }

  public void Func()
  {
    try
    {
      // do something
    }
    catch (Exception ex)
    {
      // call the provided logger dependency
      this.logger.WriteError(ex);

      // not the static singleton property
      Logger.Instance.WriteError(ex);
    }
  }
}
于 2012-08-21T19:31:37.090 回答
0

我不会滚动你自己的。我使用企业库来满足我几乎所有的日志记录需求。它适用于桌面和 asp.net 项目。Asp.net 可能会有点问题,因为您必须处理服务器上的安全问题,但我已经做到了。

http://entlib.codeplex.com/

人们也喜欢 Log4Net,但我从未使用过它,所以我无法评论它。

于 2012-08-21T19:21:53.617 回答