Typescript 测试驱动开发 TDD (13)

发布时间 2023-09-22 13:52:41作者: 天涯上过客

Jest 监视器  (Jest spies)

Jest还提供了一种能够检查特定类方法是否被调用的能力,使用的是所谓的spy。考虑以下类定义:

1 class MySpiedClass {
2    testFunction() {
3         console.log(`testFunction() called`);
4         this.testSpiedFunction();
5    }
6    testSpiedFunction() {
7         console.log(`testSpiedFunction called`);
8    }
9 }

这里,我们有一个名为MySpiedClass的类,它有两个方法。第一个方法叫testFunction,第二个方法叫testSpiedFunction。请注意,testFunction函数会将一条消息记录到控制台,并在方法体内调用testSpiedFunction。为了正确测试这段代码,我们需要一个测试来确保在调用testFunction方法时会调用testSpiedFunction。可以通过使用spy来实现此目的,如下所示:

1 it("should call testSpiedFunction", () => {
2     let mySpiedClass = new MySpiedClass();
3     const testFunctionSpy = jest.spyOn(
4             mySpiedClass, 'testSpiedFunction');
5 
6     mySpiedClass.testFunction();
7     expect(testFunctionSpy).toHaveBeenCalled();
8 });

在这里,我们有一个测试,它首先通过创建MySpiedClass的实例并将其分配给名为mySpiedClass的变量来开始。然后,我们创建了一个名为testFunctionSpy的变量,并将jest.spyOn调用的结果赋值给它。jest.spyOn函数接受两个参数。第一个参数是我们想要进行间谍操作的类实例,第二个参数是我们希望在其中创建监视器的类方法名称。在这个测试中,我们已经对testSpiedFunction方法进行了监视器操作,在调用testFunction方法时将被调用。

在这种情况下,我们对测试的期望是testFunctionSpy(对testSpiedFunction方法进行监视)已被调用。因此,使用显示器允许我们测试代码路径并确保当我们期望时它们调用其他方法。此测试的输出如下:

在这里,我们可以看到从类方法中调用了两个控制台日志。当我们调用testFunction方法时,它会将第一条消息“testFunction() called”记录到控制台。然后,testFunction方法调用了testSpiedFunction方法,该方法将第二条消息“testSpiedFunction called”记录到控制台。

请注意,我们还可以在监视器上提供一个模拟函数实现。考虑以下测试:

1 it("should call mock of testFunction", () => {
2     let mySpiedClass = new MySpiedClass();
3     const testFunctionSpy = jest.spyOn(
4          mySpiedClass, 'testFunction')
5          .mockImplementation(() => {
6               console.log(`mockImplementation called`);
7          });
8     );
9 });

在这里,我们使用了mockImplementation函数来为我们的spy提供一个在测试期间将被调用的函数实现。这个模拟实现将会向控制台输出一条消息,显示它将会被调用而不是类方法。此代码的输出如下:

在这里,我们可以看到调用了testFunction方法的模拟实现而不是testFunction方法的实际实现。这种行为很有趣值得注意。

当我们在一个方法上创建一个监视器时,我们能够检查该方法是否被调用以及是否使用了正确的参数。然而,创建一个监视器并不能阻止该方法的主体被执行。如果我们想要覆盖该方法的主体,并且不允许其被调用,则需要提供一个模拟实现。

编写测试时,判断方法体是否被调用非常重要。举个例子,假设一个方法将连接到数据库,运行查询并返回结果。在这种情况下,我们不希望运行方法的主体部分,因为我们没有数据库实例可以连接。我们希望完全模拟与数据库的任何交互。在这些情况下,我们需要提供一个模拟实现。