Javascript 如何对只调用其他函数的函数进行单元测试?

Javascript 如何对只调用其他函数的函数进行单元测试?,javascript,unit-testing,Javascript,Unit Testing,我的代码库(遗留)中有一个函数,它具有: function test() { method1("#input a") method2("test") method3(1,2) } 考虑到它调用其他方法的事实,如何为这类函数编写良好的单元测试?首先,我认为您描述的这种行为甚至不需要进行单元测试。 但是,如果您确实需要检查方法是否使用特定参数(甚至任何参数)调用。 有一种方法可以做到这一点,可以使用类似ShortifyPunit的模拟框架: 按照以下步骤操作: 确保附加到可以模拟的对象的方

我的代码库(遗留)中有一个函数,它具有:

function test()
{
 method1("#input a")
 method2("test")
 method3(1,2)
}

考虑到它调用其他
方法的事实,如何为这类函数编写良好的单元测试?

首先,我认为您描述的这种行为甚至不需要进行单元测试。 但是,如果您确实需要检查方法是否使用特定参数(甚至任何参数)调用。 有一种方法可以做到这一点,可以使用类似ShortifyPunit的模拟框架:

按照以下步骤操作:

  • 确保附加到可以模拟的对象的方法
  • 将模拟对象注入类并存根方法
  • 您可以验证()方法是使用特定参数调用的
  • 例如,如果您的类正在使用依赖项注入,这对于以下类之类的单元测试至关重要:

    class SomeClass {
    
        private $obj;
    
        public function __construct($obj) {
            $this->obj = $obj;
        }
    
        public function test()
        {
            $this->obj->method1("#input a")
            $this->obj->method2("test")
            $this->obj->method3(1,2)
        }
    }
    
    您可以编写如下的单元测试:

    public function testMethodCalled()
    {
        $mockedObj = ShortifyPunit::mock('object');
    
        $class = new SomeClass($mockedObj);
    
        // Stub the methods so you could verify they were called
        ShortifyPunit::when($mockedObj)->method1(anything())->returns(1);
        ShortifyPunit::when($mockedObj)->method2(anything())->returns(2);
        ShortifyPunit::when($mockedObj)->method3(anything(), anything())->returns(3);
    
        $class->test(); // run test() method
    
        // Verify it was called
        $this->assertTrue(ShortifyPunit::verify($mockedObj)->method1(anything())->calledTimes(1));
        $this->assertTrue(ShortifyPunit::verify($mockedObj)->method2(anything())->calledTimes(1));
        $this->assertTrue(ShortifyPunit::verify($mockedObj)->method3(anything(), anything())->calledTimes(1));
    }
    
    考虑到它调用其他
    方法的事实,
    (…)

    不管它叫什么。从消费者(呼叫者)的角度来看,最终结果非常重要。你总是测试最终结果。调用其他一些方法的事实与实现细节无关(在大多数情况下)

    我建议做一个简单的练习——将
    method1
    method2
    method3
    主体内联到
    test
    方法中。您在确定要测试的内容时会遇到问题吗

    您希望测试公共契约或可观察行为—方法调用的最终结果对调用它的人可见(许多不同的参与者可能会调用您的方法—程序的其他部分、单元测试或系统用户;每个人都应该经历相同的可观察行为)

    现在,回到多次提到的“最终结果”/“方法的可观察行为”。它可以是三件事之一:

    • 返回值(您的方法不这样做)
    • 调用第三方组件(您的方法可能正在这样做)
    • 系统内部状态的变化(您的方法似乎正在这样做)

    您需要确定最终结果并进行测试。

    这是一个非常好的问题,我也为此挣扎了很长时间。在本例中,您有一个要测试的函数
    test
    ,但该函数有依赖项-
    method1-3
    。 基本上有两种方法-

  • 保持代码不变,只需测试
    test
    功能即可
  • 将依赖项作为参数传递-
  • 范例-

    function test(method1, method2, method3) {
     method1("#input a")
     method2("test")
     method3(1,2)
    }
    
    第二种方法将为您提供模拟方法1、2和3的灵活性

    它也可能是案例一和案例二的混合,其中一些方法直接从全局范围使用,一些作为参数传递

    现在唯一的问题是理解什么时候使用什么。这里有一些提示-

    对于案例一

  • 依赖项是简单的同步函数。在JS中,异步函数通常会做一些与IO相关的工作,或者有时会进行非常昂贵的计算,这两项工作都不应该妨碍对
    test
    函数的测试
  • 它接受输入参数并返回输出
  • 它们不会在应用程序中的任何位置改变任何状态或发送任何事件
  • 对于所有其他情况,您都希望将依赖项作为参数传递。例如,在本例中,看起来
    method1
    method2
    正在执行一些DOM操作,这意味着它们应该被注入和模拟,因为as
    method3
    看起来非常简单,可以从全局范围使用

    function (d) {
      d.method1("#input a")
      d.method2("test")
      method3(1,2)
    }
    

    如果依赖项太多,可以传递包含所有依赖项的对象

    我的观点是,对于这样的函数,不需要单元测试,只测试内部方法就足够了。真的足够了吗?更改此函数内的调用顺序将改变其行为。一般来说,您的建议很好,但至少在JS中,您可以不使用对象直接模拟函数。