C# 仅仅为了单元测试而添加代码是否不好?
我正在编写一个类来帮助我对代码进行单元测试。看起来是这样的:C# 仅仅为了单元测试而添加代码是否不好?,c#,unit-testing,C#,Unit Testing,我正在编写一个类来帮助我对代码进行单元测试。看起来是这样的: /// <summary> /// Wrapper for the LogManager class to allow us to stub the logger /// </summary> public class Logger { private static ILogger _logger = null; /// <summary> /// This should
/// <summary>
/// Wrapper for the LogManager class to allow us to stub the logger
/// </summary>
public class Logger
{
private static ILogger _logger = null;
/// <summary>
/// This should be called to get a valid logger.
/// </summary>
/// <returns>A valid logger to log issues to file.</returns>
public static ILogger GetLogger()
{
if (_logger == null)
_logger = LogManager.GetLogger("logger");
return _logger;
}
/// <summary>
/// This is used by unit tests to allow a stub to be used as a logger.
/// </summary>
/// <param name="logger"></param>
/// <returns></returns>
public static ILogger GetLogger(ILogger logger)
{
_logger = logger;
return _logger;
}
}
//
///LogManager类的包装器,允许我们对记录器进行存根
///
公共类记录器
{
专用静态ILogger _logger=null;
///
///应调用此函数以获取有效的记录器。
///
///用于将问题记录到文件的有效记录器。
公共静态ILogger GetLogger()
{
如果(_logger==null)
_logger=LogManager.GetLogger(“logger”);
返回记录器;
}
///
///单元测试使用它来允许存根用作记录器。
///
///
///
公共静态ILogger GetLogger(ILogger记录器)
{
_记录器=记录器;
返回记录器;
}
}
第二种方法仅用于单元测试。我从未打算在我的生产代码中调用它
这是坏习惯吗?我是否应该找到另一种不这样做的方法?在我看来,是的,这是一种不好的做法。单元测试的目的是测试代码的实现,而不是真正影响代码的实现。在某些情况下,我发现以一种更容易/更彻底地测试的方式组织我的代码/方法是可行的,但在测试中为特定用途而测试的类中编写代码则是另一回事。任何时候你在文件/库/任何不打算在生产能力中使用的代码中都会出现代码膨胀。生产对象的编码不应支持单元测试,而应实现单元测试以支持生产对象你可以考虑这一点:
/// <summary>
/// Wrapper for the LogManager class to allow us to stub the logger
/// </summary>
public class Logger
{
private static ILogger _logger = null;
/// <summary>
/// This should be called to get a valid logger.
/// </summary>
/// <returns>A valid logger to log issues to file.</returns>
public static ILogger Logger
{
get
{
if (_logger == null)
{
_logger = LogManager.GetLogger("logger");
}
return _logger
}
set
{
_logger = value;
}
}
}
//
///LogManager类的包装器,允许我们对记录器进行存根
///
公共类记录器
{
专用静态ILogger _logger=null;
///
///应调用此函数以获取有效的记录器。
///
///用于将问题记录到文件的有效记录器。
公共静态ILogger记录器
{
得到
{
如果(_logger==null)
{
_logger=LogManager.GetLogger(“logger”);
}
返回记录器
}
设置
{
_记录器=值;
}
}
}
如果尚未使用setter设置getter,那么它会在第一次调用getter时设置记录器。我不会说这段代码本身就是一个问题。只要您在xmldoc注释中声明该方法仅用于单元测试,任何(理智的!)人都不会在生产代码中使用它
不过,如果可能的话,尽量将这些东西保存在单元测试库中。正如Joel Etherton所说,代码膨胀,在任何情况下…通常,代码的存在只是为了方便单元测试 在这种情况下,您隐藏的缺陷是使用。使用记录器的类秘密地依赖于它:除非您阅读它们的源代码,否则无法判断它们是否需要记录器
这些类应该在其构造函数中需要一个
ILogger
,使它们需要的工作变得显而易见,并且使它们易于测试。IMO,是的,这是糟糕的代码。原因有二:
- Logger了解LogManager。这将记录器与LogManager紧密耦合,并显示您遇到的问题;您必须使用LogManager来获取ILogger对象,并且不能存根或模拟LogManager
- 您向生产类添加代码的唯一目的是进行单元测试。这将使开发人员感到困惑,他们看到了测试挂钩,并认为可以在生产代码中使用它
- 定义一个公开GetLogger()的接口ILogManager,并在LogManager上实现它。然后,在Logger上创建一个类型为ILogManager的属性(或者在实例化Logger时要求传入一个属性),并从Logger范围之外(通过IoC框架,或者简单地从实例化Logger的任何对象)向Logger提供LogManager。这是松耦合;您现在可以提供实现ILogManager的任何类,包括MockLogManager,而Logger不知道其中的区别
- 出于单元测试的目的,从Logger派生一个“代理”,并在那里实现任何仅测试的方法。我使用此方法主要是对类的受保护方法进行单元测试,但如果您发现自己无法将LogManager与Logger解耦,则至少可以在测试程序集中隐藏此派生代理及其挂钩,因为生产代码不应该在测试程序集中引用它们
是的,设计并不完美:(
GetLogger
方法实际上设置了记录器!使用setter方法-这看起来更合适
顺便说一句,你测试什么?如果你使用.NET标准记录器-你可以添加或删除监听器(我记得),如果你不需要测试来编写东西-配置适当的日志监听器
你看过Moq库吗?我喜欢模仿它。我没有任何奇怪的构造函数。相反,我在视图中有一个私有变量:
private readonly ILog _log = LogManager.GetLogger(typeof(MyViewClassName));
在这种情况下,它在catch块内被调用:
_log.Error("SomePage.aspx.cs Page_Load failed", ex);
log4net没有包装器。ILog是log4net的一部分。我很好奇为什么你不使用mock。这很有意义,然后你就不需要做你正在做的事情了。回答“仅仅为单元测试添加代码不好吗?”测试性是系统的一个重要特性,因为它有助于保证系统的正确性(通过实际测试),并且它与其他良好的设计原则高度相关,如松耦合和高内聚性 从另一个角度来看;如果构建代码有两种类似的方法,主要区别在于可测试性,那么选择更易测试的方法
但是对于您的代码的具体情况,我同意这里的大多数其他帖子;有一种更好的方法来编写代码。这里的错误是,获取记录器的代码必须知道logg是什么