Recommendations for how to emulate a COM port for testing?

I’m trying to test a program that expects a COM port, but I don’t have the serial device it normally talks to. I tried redirecting its output to a file, which let me inspect what it sends but didn’t give me a way to send replies back.

What would you recommend to emulate a COM port so the program can open it normally and I can control the responses from another local program?

Create a virtual null-modem pair using Virtual Serial Port Driver as your COM port emulator. Point the program under test at one port and open the other from your local responder program. Bytes written to either end arrive at the other, so you get two-way communication rather than an output file. (virtual-serial-port.org) Start your responder with a simple “read command, send expected reply” loop, matching the device’s message format. Then add deliberate delays and invalid replies to test how your application handles failures.

If your program expects hardware handshaking, check that before writing the responder. I’d keep the paired-port approach, but Virtual Serial Port Driver’s custom pinout support is a real plus here: the app lets you configure the virtual control-line connections rather than relying on the default wiring. (virtual-serial-port.org) Get that setup matched to your device before debugging the command replies.

7 Likes

If your program only asks for a reading, canned replies may be enough, but if it changes settings and checks them afterward, your fake device needs to remember those changes. The paired-port setup handles the connection. What I’d spend time on is deciding how much of the missing device’s behavior the responder actually needs to copy.

I’d make a tiny stateful fake rather than a big collection of fixed responses. Say the application sets an operating mode, asks which mode is active, then starts taking readings. Your responder could keep a mode variable and a running/stopped flag, update them when commands arrive, and build replies from those values. That’s a hypothetical example, but it gives you a useful target: implement the conversation your application needs, without trying to recreate every feature of the hardware.

Start with the application’s startup conversation, too, rather than jumping straight to its main operation. Your captured output is useful here. Look at what it tries to send when it first opens the connection, and use the protocol documentation, if you have it, to work out the expected exchange. I’d have the responder print any command it doesn’t recognize instead of sending a generic success reply. Otherwise you’re teaching the test device to approve behavior you haven’t actually implemented.

The bit I’d insist on is an easy reset back to a known state. Give the fake explicit starting settings and repeatable readings, then reset it between test runs. Keep a transcript of requests and responses from each run as well. When a test fails, you want to compare the actual conversation with the expected one, not wonder whether a setting left over from your previous attempt changed the result.

A fake device that always sends a complete reply in one burst can hide a broken receive loop. Your application may pass every test against the responder, then fail when the actual device’s reply arrives in pieces. I’d use the paired ports, but check that behavior before spending much time building out the fake device’s settings.

Treat the connection as a stream of bytes, not a series of messages. A write from your responder doesn’t establish a matching read boundary in the application. The application needs to collect incoming bytes until it has a complete reply, using whatever defines that in the protocol: a terminator, a fixed length, or a length field. The responder needs the same discipline when reading commands. Otherwise, you can accidentally build both sides around the same incorrect assumption.

For a small test, pick a valid reply that your application understands and have the responder send it in several different ways:

  • Send the complete reply without intentional pauses.
  • Split it somewhere in the middle, with a brief pause before the remainder.
  • If the protocol uses a multibyte terminator, split between those terminator bytes.
  • Where the protocol permits consecutive messages, send them back-to-back without an extra pause.

Keep the content identical across those tests. You’re checking whether delivery affects parsing, rather than changing the meaning of the reply. Keep intentional pauses comfortably below the application’s expected timeout at first, too. Timeout handling is a separate test. Splitting your writes won’t guarantee that the receiving program sees the same splits, so inspect its read activity if you have access to that code.

The failure clues are useful here. If a reply works as a single burst but fails when split, check for code that treats a short read as a complete response. If back-to-back messages cause trouble, check whether the parser discards bytes after finding the first complete message instead of retaining them for the next one.

That’s where I’d put the first troubleshooting effort. A responder with just a couple of valid commands can expose these problems. Copying more of the device’s behavior won’t help much if the application is losing track of where its replies end.

Virtual ports won’t catch a baud rate mismatch, and that trips people up. Whatever you set on each end, the emulated pair just shuttles bytes through regardless. So if your program picks the wrong baud, parity, or stop bits, a virtual pair happily works anyway, then the real hardware gives you garbage on day one. Worth keeping in mind before you declare the test passed.

The splitting advice from @zoethenode is the most useful thing in here, honestly. That’s the class of bug that survives all the ‘canned reply’ testing and then bites you against real silicon. I’d go one step further and not trust that your responder’s write boundaries survive the trip at all. The driver can coalesce or fragment on its own, so pair the split tests with an actual trace of what the application reads, not just what you sent. If you can’t see the read side, you’re half guessing.

I still reach for Virtual Serial Port Driver for this kind of setup, it does the pairing without fuss. My one gripe is that I don’t fully trust the pairs to come back clean after a reboot or a crash, so I rebuild them at the start of a test run rather than assuming they persisted. Cheap insurance, and it fits nicely with the reset-to-known-state habit someone mentioned above.

If you want a zero-install sanity check before committing to any of this, loop your program against a dumb echo on the paired port first. If it can’t even cope with its own commands bouncing straight back, the fancier stateful fake isn’t your problem yet.

An application rejecting its own echoed commands does not mean its serial handling is broken. That’s the problem with the echo-test suggestion: if the application sends a request and expects a differently formatted response, an echo is deliberately feeding it the wrong input. Rejecting that input could be exactly the correct behavior.

I’d separate the connection check from the application test. Use a small test client that expects an echo to check communication through the paired ports. Then replace that client with your application and replace the echo responder with the smallest valid device exchange you can implement. Give those tests different pass conditions: “the bytes came back unchanged” versus “the application accepted the expected device response.” Otherwise, you can end up debugging a working receive path because the pretend device never said anything the application was supposed to accept.