Mock SerialPort Response

28 Views Asked by At

I have a client class that opens a connection to a Serial Port. I want to write unit tests against this class in a way that when my client class sends data to the mocked serial port and the test can respond with data so the client class can continue. In other words I want to test basically a request/response over a serial connection without an actual devices.

SignClient.ts

export class SignClient {
    private readonly DEFAULT_BAUD_RATE = 9600;
    private readonly TIMEOUT = 5000;

    serial?: SerialPortStream;
    comPort: string;
    baudRate: number;

    private binding?: BindingInterface;
    private timeout?: NodeJS.Timeout;
    parser?: SignClientResponseParser;

    constructor(comPort: string, baudRate?: number, binding?: BindingInterface) {
        this.comPort = comPort;
        this.baudRate = baudRate || this.DEFAULT_BAUD_RATE;
        this.binding = binding;
    }

    async connect(): Promise<SignClient> {
        return new Promise<SignClient>((resolve, reject) => {
            this.serial = new SerialPortStream({
                binding: this.binding || SerialPort.binding,
                path: this.comPort, 
                baudRate: this.baudRate
            }, (err) => {
                if (err !== null) { 
                    this.serial = undefined;
                    reject(err); 
                    return; 
                }
                this.parser = this.serial?.pipe(new SignClientResponseParser());
                resolve(this);
                return;
            });
            
        });
    }

    async send<T extends Response>(packet: TransmissionPacket): Promise<T> {
        return new Promise<T>((resolve, reject) => {
            if (this.serial === undefined) { 
                throw new Error("Serial port is not open");
            }
            if (packet.expectsResponse) {
                const responseListener = this.parser?.on('data', async (data) => {
                    responseListener?.removeAllListeners();
                    clearTimeout(this.timeout as NodeJS.Timeout);
                    this.timeout = undefined;
                    try {
                        const response = await ResponseFactory.parse(data);
                        resolve(response as T);
                    }
                    catch (err) {
                        reject(err)
                    }
                });
                const errorListener = this.parser?.on('error', (err) => {
                    errorListener?.removeAllListeners();
                    reject(err);
                });
            } 
            this.serial.write(packet.toBuffer(), (err) => {
                if (err !== undefined) { reject(err); return; }
                
                if (packet.expectsResponse) { 
                    this.timeout = setTimeout(() => {
                        this.serial?.removeAllListeners('data');
                        reject("Timeout");
                        return;
                    }, this.TIMEOUT);
                    return; 
                }

                resolve(undefined as unknown as T);
                return;
            });
        });
    }

    isOpen(): boolean {
        return this.serial?.isOpen || false;
    }
}

I have hacked a version of a test using a time out here:

beforeEach(() => {
        SerialPortMock.binding.createPort('/dev/serial1', { echo: false, record: false })
    });

    afterEach(() => {
        SerialPortMock.binding.reset();
    });

    test('should send and listen for a malformed packet', async () => {
        const signClient = await new SignClient('/dev/serial1', undefined, MockBinding).connect();
        setTimeout(() => {
            signClient.parser?.emit('data', Buffer.from(malformedPacket));
        }, 100);
        await expect(signClient.send(new TestTransmissionPacket(true))).rejects.toThrowError();
    });

Is there a way to hook into the mocked serial port so when my SignClient class writes data I can respond on that serial port with test data?

0

There are 0 best solutions below