javascript - Làm thế nào tôi có thể giả lập một thuộc tính riêng trong một lớp mà tôi đang cố gắng kiểm thử bằng jest
Tôi có một phương thức lớp mà tôi muốn kiểm tra:
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
Mỗi lệnh digitalWrite này được thực hiện trên một thể hiện của một lớp khác nhau được tạo ra khi lớp của tôi được khởi tạo:
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),
},
}
}
Tôi có thể tạo một bản giả lập cho thuộc tính stepperMotors trong bài kiểm tra của mình với các bản giả lập của lớp Gpio (tôi đã giả lập constructor cho một số bài kiểm tra khác):
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)
Nhưng tôi không thể vì thuộc tính stepperMotor là riêng tư.
Có một số cách tôi có thể giải quyết vấn đề này (biến thuộc tính thành public, tạo một phương thức public để thiết lập thuộc tính), nhưng không cách nào trong số đó có vẻ khả thi bởi vì thuộc tính này không bao giờ nên được truy cập từ bên ngoài lớp nên tôi sẽ phải tiết lộ các thuộc tính hoặc phương thức chỉ để hỗ trợ việc kiểm thử.
Có cách nào khác để thực hiện loại kiểm tra này không? Tôi biết trong Jest tôi có thể giả lập một phương thức trên một lớp trong JavaScript bằng cách thay thế hàm prototype ví dụ:
BotController.prototype.someMethod = jest.fn()
const controller = new BotController
Và nếu đây là một lớp mà tôi đang cố gắng giả lập, tôi có thể truyền các thuộc tính như một triển khai giả, ví dụ:
jest.mock("../BotController", () => ({
stepperMotors: mockStepperMotorConfiguration
}))
Nhưng sau đó mọi thứ khác trong lớp cũng sẽ bị chế giễu và bạn sẽ mất đi mục đích.
Bạn có ý tưởng nào về cách tôi nên tiếp cận việc này không?
Cập nhật: đang cố gắng tạo cửa hậu
Tôi đang thử phương pháp của Taplar trong việc tạo cửa sau.
Tôi đã thử ép kiểu instance của bộ điều khiển của mình thành any:
Nhưng trình biên dịch vẫn đang la tôi:
Cập nhật khác
Sau khi Taplar chỉ ra cách gọi phương thức trên phiên bản ép kiểu, các lỗi trên cửa sau đã biến mất, điều này thật tuyệt vời!
Bức tường tiếp theo mà tôi đập vào là bây giờ vì lý do nào đó bài kiểm tra không thấy mock của tôi nữa, điều này thật kỳ lạ vì biến này là local đối với bài kiểm tra.
Câu trả lời #1
Trừ khi sử dụng # để đảm bảo quyền riêng tư nghiêm ngặt, các thuộc tính riêng tư có thể được truy cập bên ngoài lớp khi chạy, các bộ chỉnh sửa truy cập của TypeScript chỉ được áp dụng tại thời điểm biên dịch.
Truy cập các thành viên riêng tư trong các bài kiểm tra có thể được coi là phản chiếu.
Khả năng truy cập có thể bị bỏ qua bằng cú pháp dấu ngoặc, đây là lựa chọn được ưu tiên:
controllerInstance['stepperMotors'] = ...;
Hoặc với API Reflect:
Reflect.set(controllerInstance, 'stepperMotors', ...);
Hoặc bằng cách tắt kiểm tra loại:
(controllerInstance as any).stepperMotors = ...;
Vì thuộc tính riêng tư được thiết lập với phương thức prototype, một cách tiếp cận khác là giả lập nó. Điều này áp dụng nếu việc khởi tạo ban đầu gây ra các tác dụng phụ không mong muốn và cần được tránh. BotController.prototype.someMethod=jest.fn() không bao giờ nên được sử dụng trong Jest vì nó không thể được dọn dẹp tự động và làm nhiễm bẩn các bài kiểm tra khác. Thay vào đó, nó có thể được:
jest.spyOn(BotController.prototype, 'initalizeMotors').mockImplementation(function (this: BotController) {
this['stepperMotors'] = ...;
});
...
expect(controllerInstance['initalizeMotors']).toHaveBeenCalled();


