2021-07-22

Reading from files, single responsibility principle, and testability

Say that your program needs to read some kind of structured data from a file. In a object oriented language the obvious thing to do is encapsulate the parser and the storage of its results in a class. Perhaps something like:

class DataReader { public: DataReader(std::string filename) { std::ifstream in("filename") if ( in.good() ) { parse(in); } } //... }

Indeed some of the legacy code my project calls had one of these as the main access point for a bunch of important functionality, and I've been asked to extend it and authorized to do some re-factoring along the way. Whoohoo!

But I can't afford to break existing code (or at least I have to know what I'm breaking and why...), which means I need some detailed test. Yes. It doesn't have a test suite. I mentioned that it was legacy code, didn't I? And that is where the problems start, because how do you write a self-contained test for DataReader?

  • Actually creating a (or more likely several) test files break self-containment and means your test code needs to know where to look for the files, and test can fail for reason that have nothing to do with DataReader. Yuck!
  • You could encode the test input as strings in your tests, create and write a temporary file, run the test on that file, then clean up the temporary file. That solves the self-containment and "where to find it" problems and doesn't clutter up the disk with a large number of similar test files. But it still breaks if there is a problem with the disk. More problematically all the set-up and tear-down code is a place to write bugs, and interferes with the clarity of your test. Still pretty bad.
  • The third option is to refactor to make it clear that opening the file and dealing with the contents are separate "thing"s for the purposes of the Single Responsibility Principle. The class should take a stream not a filename. Now to avoid breaking existing code we can relax our pedantic insistence of separation of concerns by leaving the existing c'tor as a convenience but defering most of the work to a new c'tor that takes a stream. Something like: class DataReader { public: DataReader(std::istream in) { if (in.good()) { parse(in); } } DataReader(std::string filename) : Datareader(std::ifstream(filename)) {} //... } Then we use a std::istringstream in the test eliding much of the setup and all of the tear-down.

I shouldn't have to ?*(<|#% register to do that!

The house we bought out here is not only bigger than any we've had before but it is bigger than we actually needed. There just wasn't anything smaller that met our needs on the market when we had to buy, so we had to take what was available.

The shear size of the place has been a problem from a WiFi perspective.1 There has been signal and bandwidth everywhere, but in some corners both sometimes drop pretty low. One of those corners is the guest bedroom which is where I work-from-home.

Well, complaints from my better half finally got me off my duff. I bought a couple of meshing extenders. Nice ones that maintain the same SSID none-the-less. The instructions included in the box tell me to download the app and use it to configure the widgets. I grumbled a bit, but fine. Except that once I have the app I learn that it requires me to set up an account with the manufacturer's web site.

Are you kidding me? Why would I want to do that?

It's a serious question. What makes them think I want to register?

In a word: No!

Now, it turns out these widgets support a WPS pairing mechanism.2

However, that wasn't mentioned in the material in the box and was pushed to the very bottom of the help web page: a tiny paragraph of plain text under screen-fulls of colorful pictures and enumerated lists of steps supporting the two ways to register with them to get it done.

I can only assume that this is done in bad faith. It is a malicious act in the service of the company to the detriment (admittedly small) of their customers and it makes the world a tiny bit worse.

Worse, I think the printed docs included the no-registration instructions at some time (the controls for it are labeled on the diagram but never referred to). At some point a marketing jerk told the tech writers to make the documentation worse and the tech writers complied, but they didn't take the time to scrub all the traces of their lost efforts.


1 The house actually has some kind of wire-in-the-wall-and-RJ45-jacks system from Honeywell pre-installed, but we didn't get the printed docs from the previous owner and I have never figured it out. Naively it looks like some kind of star topology that assumes you're putting the control next the main panel (i.e in the laundry room). So we're living with wireless. Besides there are relatively few ports and most of them are in inconvenient places.

2 Just as well for them. I'd have sent them back rather than register. And so should you. Fight this nonsense.

2021-07-10

Well, obviously...

We've had my in-laws here for the last week with our nephew for "Camp Cousins" to ensure that the youg'uns know one another. Lots of activities around the house: arts, crafts, games, and even an intoduction to Roblox development for the eight year old (which is why it had to be here and not in California where the in-laws live); and trips out to the zoo, aquarium, science museum, and even putt-putt.

I had shirts made. But they took longer than expected to come, so we didn't get them until Thursday and couldn't wear the to the zoo for the planned pictures. We rescheduled that for today (the last day). When the time came we headed out to the (new) planned location (where we could include the same mountains in the background as I had put on the shirts), only to find that it was starting rain (out of season) even as the dust storm continued (very much in season).

Rain and dust storm. Of course.