Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 仅仅为了单元测试而添加代码是否不好?_C#_Unit Testing - Fatal编程技术网

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是什么