javascript - 如何在我试图开玩笑测试的类中模拟私有属性
问题描述
我有一个要测试的类方法:
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
调用中的每一个都是对构造我的类时创建的不同类的实例进行的:
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),
},
}
}
我可以在我的测试中使用类的模拟为该stepperMotors
属性创建一个模拟Gpio
(我已经在模拟其他一些测试的构造函数):
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
物业是私人的。
有几种方法可以解决这个问题(使属性公开,设置属性的公开方法),但它们似乎都不理想,因为该属性永远不能在类之外访问,所以我只会公开属性或方法支持测试。
还有其他方法可以进行这种测试吗?我开玩笑地知道我可以通过替换原型函数来模拟 javascript 中的类上的方法,例如:
BotController.prototype.someMethod = jest.fn()
const controller = new BotController
如果这是一个我试图模拟的类,我可以将属性作为模拟实现传递,例如:
jest.mock("../BotController", () => ({
stepperMotors: mockStepperMotorConfiguration
}))
但是,课堂上的其他所有内容也会被嘲笑,你就会失去重点。
关于我应该如何处理这个问题的任何想法?
更新:试图创建一个后门
我正在尝试 Taplar 创建后门的方法。
我尝试将我的控制器实例转换为any
:
但是编译器还在对我大吼大叫:
另一个更新
在 Taplar 指出如何在演员版本上调用该方法之后,错误在后门消失了,这太棒了!
我撞到的下一堵墙是,由于某种原因,现在测试无法再看到我的模拟了,这很奇怪,因为该变量是测试的本地变量。
解决方案
除非使用#
硬隐私,否则私有属性可以在运行时在类外部访问,TypeScript 访问修饰符仅在编译时应用。
在测试中访问私有成员可以被认为是一种反射。
可以用括号表示法绕过可见性,这是更好的选择:
controllerInstance['stepperMotors'] = ...;
或者使用反射 API:
Reflect.set(controllerInstance, 'stepperMotors', ...);
或者通过禁用类型检查:
(controllerInstance as any).stepperMotors = ...;
由于私有属性是用原型方法设置的,另一种方法是模拟它。如果原始初始化导致不良副作用并且需要避免,则适用。BotController.prototype.someMethod = jest.fn()
永远不要在 Jest 中使用,因为它不能自动清理并交叉污染测试。相反,它可能是:
jest.spyOn(BotController.prototype, 'initalizeMotors').mockImplementation(function (this: BotController) {
this['stepperMotors'] = ...;
});
...
expect(controllerInstance['initalizeMotors']).toHaveBeenCalled();
推荐阅读
- php - 严重性:警告消息:非法字符串偏移 'status' 和消息:非法字符串偏移 'transaction_details'
- python - tkinter python 更改列表框文件的名称而不更改该文件的文件目录
- arrays - 如何成对比较Ruby中的两个数组?
- pytorch - 带有 nn.Linear 的错误 CUBLAS_STATUS_NOT_INITIALIZED
- android - android缓冲区大小错误上的Tensorflowlite
- linux - 如何在 Windows 上使用 Mac 和 Linux 的“nwjc”版本?
- angular - 订阅组件正在工作,但异步管道未订阅可观察的
- java - foobar.withgoogle.com 任务请通过编码消息 java 测试 4 失败
- html - 如何将组件的html嵌入到另一个组件的html中
- r - 我如何写为 S4 类的 csv 文件?