我如何在我要开玩笑地测试的类中模拟私有属性 [英] How can I mock a private property in a class I'm trying to test in jest

查看:56
本文介绍了我如何在我要开玩笑地测试的类中模拟私有属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个要测试的类方法:

I have a class method I want to test:

setStepResolution(resolution: stepResolution): void {
        switch (resolution) {
            case stepResolution.FULL_SETUP:
                this.stepperMotors.left.ms1Pin.digitalWrite(0)
                this.stepperMotors.left.ms2Pin.digitalWrite(0)
                this.stepperMotors.left.ms3Pin.digitalWrite(1)
                this.stepperMotors.right.ms1Pin.digitalWrite(0)
                this.stepperMotors.right.ms2Pin.digitalWrite(0)
                this.stepperMotors.right.ms3Pin.digitalWrite(1)
                break
            case stepResolution.HALF_STEP:
                this.stepperMotors.left.ms1Pin.digitalWrite(1)
                this.stepperMotors.left.ms2Pin.digitalWrite(0)
                this.stepperMotors.left.ms3Pin.digitalWrite(0)
                this.stepperMotors.right.ms1Pin.digitalWrite(1)
                this.stepperMotors.right.ms2Pin.digitalWrite(0)
                this.stepperMotors.right.ms3Pin.digitalWrite(0)
                break

每个digitalWrite调用都针对构建我的类时创建的不同类的实例:

Each of these digitalWrite calls is made to an instance of a different class that are created when my class is constructed:

export default class BotController {

    private stepperMotors: StepperMotorCollection

    constructor() {
        this.initalizeMotors()
    }

    private initalizeMotors(): void {
        this.stepperMotors = {
            left: {
                directionPin: new Gpio(Number(process.env.LEFT_DIRECTION_PIN), { mode: Gpio.OUTPUT }),
                stepPin: new Gpio(Number(process.env.LEFT_STEP_PIN), { mode: Gpio.OUTPUT }),
                ms1Pin: new Gpio(Number(process.env.LEFT_RESOLUTION_PIN_MS1), { mode: Gpio.OUTPUT }),
                ms2Pin: new Gpio(Number(process.env.LEFT_RESOLUTION_PIN_MS2), { mode: Gpio.OUTPUT }),
                ms3Pin: new Gpio(Number(process.env.LEFT_RESOLUTION_PIN_MS3), { mode: Gpio.OUTPUT }),
                stepsPerMM: Number(process.env.LEFT_STEPS_PER_MM),
                swapCoils: Boolean(process.env.LEFT_SWAP_COILS),
            },
            right: {
                directionPin: new Gpio(Number(process.env.RIGHT_DIRECTION_PIN), { mode: Gpio.OUTPUT }),
                stepPin: new Gpio(Number(process.env.RIGHT_STEP_PIN), { mode: Gpio.OUTPUT }),
                ms1Pin: new Gpio(Number(process.env.RIGHT_RESOLUTION_PIN_MS1), { mode: Gpio.OUTPUT }),
                ms2Pin: new Gpio(Number(process.env.RIGHT_RESOLUTION_PIN_MS2), { mode: Gpio.OUTPUT }),
                ms3Pin: new Gpio(Number(process.env.RIGHT_RESOLUTION_PIN_MS3), { mode: Gpio.OUTPUT }),
                stepsPerMM: Number(process.env.RIGHT_STEPS_PER_MM),
                swapCoils: Boolean(process.env.RIGHT_SWAP_COILS),
            },
        }
    }

我可以使用Gpio类的模拟在测试中为stepperMotors属性创建模拟(我已经在模拟其他一些测试的构造函数):

I could create a mock for the stepperMotors property in my test with mocks of the Gpio class (I'm already mocking the constructor for some of the other tests):

test("can change step resolution", () => {
        // * The step resolution of the stepper motors can be changed via the code.
        // * The settings can be controlled by an enum that denotes each of the possible
        // * resolutions.

        const mockStepperMotorConfiguration: StepperMotorCollection = {
            left: {
                directionPin: new pigpio.Gpio(1),
                stepPin: new pigpio.Gpio(1),
                ms1Pin: new pigpio.Gpio(1),
                ms2Pin: new pigpio.Gpio(1),
                ms3Pin: new pigpio.Gpio(1),
                stepsPerMM: 1,
                swapCoils: false,
            },
            right: {
                directionPin: new pigpio.Gpio(1),
                stepPin: new pigpio.Gpio(1),
                ms1Pin: new pigpio.Gpio(1),
                ms2Pin: new pigpio.Gpio(1),
                ms3Pin: new pigpio.Gpio(1),
                stepsPerMM: 1,
                swapCoils: false,
            },
        }

        // ^ To change the resolution to a full step
        // * send in the full step enum
        newController.setStepResolution(stepResolution.FULL_SETUP)

但是我不能,因为stepperMotor属性是私有的.

But I can't because the stepperMotor property is private.

有几种方法可以解决此问题(将属性设置为公共,设置属性的设置为公共方法),但是这两种方法似乎都不可取,因为该属性永远不能在类外部访问,因此我将公开属性或仅用于支持测试的方法.

There are several ways I could solve this (make the property public, make a public method for setting the property), but neither of them seems desirable because the property should never be accessible outside of the class so I would be exposing properties or methods only to support testing.

还有另一种进行这种测试的方法吗?我知道开玩笑我可以通过替换原型函数来模拟javascript类中的方法:

Is there another way of doing this kind of test? I know in jest I can mock a method on a class in javascript by replacing the prototype function e.g.:

BotController.prototype.someMethod = jest.fn()
const controller = new BotController

如果这是我要模拟的类,则可以将属性作为模拟实现传入,例如:

And if this was a class I was trying to mock I could pass in the properties as a mock implementation, e.g.:

jest.mock("../BotController", () => ({
    stepperMotors: mockStepperMotorConfiguration
}))

但是随后该类中的所有其他内容也将被嘲笑,而您将失去这一点.

But then everything else in the class would also be mocked and you'd loose the point.

关于我该如何处理的任何想法?

Any idea on how I should approach this?

我正在尝试塔普拉(Taplar)创建后门的方法.

I'm trying out Taplar's approach of creating a backdoor.

我尝试将控制器实例强制转换为any:

I tried casting my controller instance as an any:

但是编译器仍然对我大喊:

But the complier is still yelling at me:

在塔普拉(Taplar)指出了如何在转换版本上调用该方法之后,错误在后门消失了,真是太好了!

After Taplar pointed out how to call the method on the cast version the errors went away on the back door which is fantastic!

我碰到的下一堵墙是,由于某种原因,测试现在再也看不到我的模拟了,这很奇怪,因为变量在测试中是本地的.

The next wall I smashed into was that now for some reason the test can't see my mock anymore which is weird because the variable is local to the test.

推荐答案

除非 #硬隐私,可在运行时在类外部访问私有属性,TypeScript访问修饰符仅在编译时应用.

Unless # hard privacy is used, private properties can be accessed outside a class at runtime, TypeScript access modifiers are applied only at compilation time.

在测试中访问私有成员可以被认为是一种反映.

Accessing private members in tests can be considered a reflection.

可以使用括号表示法绕过可见性,这是​​更可取的选择:

Visibility can be bypassed with bracket notation, which is the preferable option:

controllerInstance['stepperMotors'] = ...;

或使用Reflect API:

Or with Reflect API:

Reflect.set(controllerInstance, 'stepperMotors', ...);

或通过禁用类型检查:

(controllerInstance as any).stepperMotors = ...;

由于私有属性是通过原型方法设置的,因此另一种方法是对其进行模拟.如果原始初始化导致不良的副作用并需要避免,则适用. BotController.prototype.someMethod = jest.fn()切勿在Jest中使用,因为它不能自动清理并交叉污染测试.相反,它可能是:

Since private property is set with prototype method, another approach is to mock it. It's applicable if original initialization causes undesirable side effects and needs to be avoided. BotController.prototype.someMethod = jest.fn() should never be used in Jest as it cannot be automatically cleaned up and cross-contaminates tests. Instead it could be:

jest.spyOn(BotController.prototype, 'initalizeMotors').mockImplementation(function (this: BotController) {
  this['stepperMotors'] = ...;
});
...
expect(controllerInstance['initalizeMotors']).toHaveBeenCalled();

这篇关于我如何在我要开玩笑地测试的类中模拟私有属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆