代码之家  ›  专栏  ›  技术社区  ›  jpoh

一种方法依赖另一种方法是否有代码味道?

  •  7
  • jpoh  · 技术社区  · 15 年前

    我正在重构一个类,以便代码是可测试的(使用nunit和rinomocks作为测试和隔离框架),并且发现我发现自己使用的方法依赖于另一个方法(即它依赖于由该另一个方法创建的某个东西)。如下所示:

    public class Impersonator
    {
        private ImpersonationContext _context;
    
        public void Impersonate()
        {
            ...
            _context = GetContext();
            ...
        }
    
        public void UndoImpersonation()
        {
            if (_context != null)
                _someDepend.Undo();
        }
    }
    

    这意味着要测试 UndoImpersonation ,我需要打电话 Impersonate (模拟已经有几个单元测试来验证其行为)。这对我来说很难闻,但从调用此类的代码的角度来看,在某种意义上说是有意义的:

    public void ExerciseClassToTest(Impersonator c)
    {
         try
         {
             if (NeedImpersonation())
             {
                 c.Impersonate();
             }
             ...
         }
         finally
         {
             c.UndoImpersonation();
         }
    }
    

    如果我不尝试编写单元测试 撤消模拟 发现自己不得不通过调用另一个公共方法来设置测试。那么,这是一种难闻的气味吗?如果是的话,我怎么解决它呢?

    6 回复  |  直到 7 年前
        1
  •  9
  •   paxdiablo    7 年前

    代码气味必须是其中之一 模糊的 我在编程界遇到过的术语。对于一组以工程原理为荣的人来说,它在不可测量的垃圾方面排名靠前,在衡量程序员效率方面,它几乎是无用的,就像每天的LOC一样。

    不管怎样,这是我的咆哮,谢谢你的倾听。

    为了回答你的具体问题,我不相信 一个问题。如果您测试的东西有预先条件,您需要确保先为给定的测试用例设置了预先条件。

    其中一个测试应该是当你调用它时会发生什么 没有 首先设置先决条件-如果呼叫者不愿意,它应该优雅地失败,或者设置自己的先决条件。

        2
  •  7
  •   sleske    15 年前

    好吧,上下文有点太少了,它看起来像是,somedependy应该在构造函数中进行初始化。

    在实例方法中初始化字段对我来说是一大禁忌。类一经构造就应该完全可用(即所有方法都可以工作);因此,构造函数应该初始化所有实例变量。请参见上的页面 single step construction 在沃德坎宁安的维基里。

    实例方法中初始化字段不好的原因主要是它对如何调用方法施加了隐式排序。在您的案例中,Themethodiwanttotest将根据是否首先调用dostuff来执行不同的操作。这通常不是您类的用户所期望的,所以它是坏的。

    也就是说,有时这种耦合是不可避免的(例如,如果一个方法获得了一个资源,例如一个文件句柄,并且需要另一个方法来释放它)。但如果可能的话,甚至应该在一个方法中处理这个问题。

    如果没有更多的背景,很难说你的案件适用什么。

        3
  •  2
  •   Steve Gilham    15 年前

    如果您不将可变对象视为代码本身的味道,那么将对象置于测试所需的状态只是该测试设置的一部分。

        4
  •  2
  •   Keith    15 年前

    这通常是不可避免的,例如在使用远程连接时-您必须拨打 Open() 在你打电话之前 Close() 你不想 打开() 在构造函数中自动发生。

    但是,在这样做时,您需要非常小心,因为模式是很容易理解的——例如,我认为大多数用户对任何事务性的行为都接受这种行为,但是当他们遇到这种行为时可能会感到惊讶。 DoStuff() TheMethodIWantToTest() (不管他们真正叫什么)。

    拥有一个表示当前状态的属性通常是最佳实践——再次查看远程连接或数据库连接,以获得一贯理解的设计示例。

    最大的禁忌是,房地产永远不会发生这种情况。属性应该 从未 关心他们的顺序。如果有一个简单的值依赖于方法的顺序,那么它应该是无参数方法而不是属性get。

        5
  •  2
  •   Wim Coenen    15 年前

    是的,我想在这种情况下有一种代码味道。不是因为方法之间的依赖性,而是因为对象的模糊标识。而不是 Impersonator 它可以在不同的角色状态下,为什么不具有不变的 Persona ?

    如果你需要不同的 人物角色 ,只需创建一个新对象,而不是更改现有对象的状态。如果你以后需要做一些清理,做 人物角色 可任意处理的。你可以保留 冒名顶替者 工厂等级:

    using (var persona = impersonator.createPersona(...))
    {
       // do something with the persona
    }
    
        6
  •  1
  •   Chris S    15 年前

    回答标题:在面向对象的编程中,让方法彼此调用(链接)是不可避免的,因此在我看来,测试调用另一个方法没有任何错误。单元测试毕竟可以是一个类,它是您正在测试的“单元”。

    链接的级别取决于对象的设计-您可以 叶栅 .

    • 分叉: classToTest1.SomeDependency.DoSomething()
    • 级联 : classToTest1.DoSomething() (内部称为someDependency.dosomething)

    但正如其他人提到的,一定要将状态初始化保存在构造函数中,据我所知,这可能会解决您的问题。

    推荐文章