Jasmine unit testing: Don't forget to callThrough()

TL;DR

One of the great things about Jasmine, the Javascript unit testing library, is the spy. A spy lets you peek into the workings of the methods of Javascript objects. Just don't forget to use callThrough() when you don't want to alter how the spied-upon function behaves. That's because a spy automatically replaces the spied function with a stub. If you want the spied-upon function to be called normally, add .and.callThrough() to your spy statement.

Jasmine spies are great

One of the great things about Jasmine, the Javascript unit testing library, is the spy. From Jasmine's documentation:

A spy can stub any function and tracks calls to it and all arguments. A spy only exists in the describe or it block in which it is defined, and will be removed after each spec.

They are extremely useful for testing and should be part of every unit test writer's tool set. However, if you forget some basic truths about spies, you will get unexpected results and wonder why your beautiful tests don't work. Some examples:

spyOn(myGreatJSLib, 'doSomething');
myGreatJSLib.doSomething();
// passes
expect(myGreatJSLib.doSomething).toHaveBeenCalled();

spyOn(myGreatJSLib, 'doAnotherThing');
myGreatJSLib.doAnotherThing('value1', 10);
// also passes
expect(myGreatJSLib.doAnotherThing).toHaveBeenCalledWith('value1', 10);

spyOn() replaces functions

Jasmine's documentation adds:

By chaining the spy with and.callThrough, the spy will still track all calls to it but in addition it will delegate to the actual implementation.

This is an important piece of information to keep in mind, because, depending on what your testing, your tests will appear to be working correctly, but your expect statements might fail unexpectedly.

Imagine we're testing our awesome RickAndMortyLib which has a getQuote(characterName) function. Supplied with the values Rick, Morty, or Summer, the function will return a quote for that character. For the sake of this example, there is only one quote per character:

  • Rick: "Oh yeah, you gotta get schwifty."
  • Morty: "Don't even trip about your pants, dawg. We got an extra pair right here."
  • Summer: "God, Grandpa, you're such a dick."

Some examples:

spyOn(RickAndMortyLib, 'getQuote');
RickAndMortyLib.getQuote('Rick');
// passes
expect(RickAndMortyLib.getQuote).toHaveBeenCalledWith('Rick');

// but....
var rickQuote = RickAndMortyLib.getQuote('Rick');
// FAILS
expect(rickQuote).toBe('Oh yeah, you gotta get schwifty.');

Why did that last expect fail? Because the spyOn() call up top replaces getQuote() with a stub which can only confirm that it was called and/or called with specific arguments. We could fake a response with one of these two techniques:

spyOn(RickAndMortyLib, 'getQuote').and.returnValue(
                   'Morty, can you get to the left nipple?');
var rickQuote = RickAndMortyLib.getQuote('Rick');
// passes
expect(rickQuote).toBe('Morty, can you get to the left nipple?');

// or....
spyOn(RickAndMortyLib, 'getQuote').and.callFake(function() {
    return 'GRAAAAAASSSSSSS....tastes bad.'
});
// passes
expect(rickQuote).toBe('GRAAAAAASSSSSSS....tastes bad.');

Those two tests pass, and the and.returnValue() and and.callFake() calls can be very useful in situations where it's impossible or difficult to supply whatever your library needs to complete the function call on its own. However, know that you're not really testing the function itself when you use a plain spyOn() call or when you chain and.returnValue() or and.callFake().

To really test the function call itself, you need to add and.callThrough():

spyOn(RickAndMortyLib, 'getQuote').and.callThrough();
RickAndMortyLib.getQuote('Rick');
// passes
expect(RickAndMortyLib.getQuote).toHaveBeenCalledWith('Rick);
var rickQuote = RickAndMortyLib.getQuote('Rick');
// now this expect passes
expect(rickQuote).toBe('Oh yeah, you gotta get schwifty.');

Programming Basics: Names matter

TLDR;

The names developers use for identifiers in their code is essential for readability, maintainability and is one of the indicators of a competent software developer.

Combined with a naming convention system, well-chosen identifier names have the potential to make or break a project, especially on collaborative endeavors with multiple developers.

What makes a good name?

A good identifier name enhances comprehension of the code it describes, making it readable, easy to follow and ultimately more maintainable.

While placing comments in code is very important, you could argue that using descriptive names for identifiers in our code greatly reduces or possibly eliminates the need for comments.

You can further magnify the effect of the identifier names you use by applying naming conventions.

What are naming conventions?

For the uninitiated, naming conventions are rule sets applied to the names of identifiers in software (ex: variables, constants, types, functions). There are many systems of naming conventions such as Hungarian Notation and Camel Case.

Here's an example of Camel Case -- where the individual words in identifier names are visually separated by capitalizing the first letter of that word. getName() instead of getname(). The camel cased version of the method is easier to read.

I personally separate the concepts of naming from naming conventions since, although similar, they aren't the same thing. Most naming conventions dictate the use of underscores, letter case and letter code combinations for use within identifier names, but they don't address the actual content or meaning of the identifier itself.

Most naming convention systems rely on the programmer applying them to logical, meaningful names. And that's the area in which, surprisingly, a lot of software developers do a poor job.

A good identifier name for a service that handled Person objects could be personservice. Applying Camel Case to it transforms it to 'PersonService'. Again, the application of a naming convention makes it easier to read and understand.

However, if instead of calling the class PersonService we had called it ps, applying camel case to it (PS) does little to aid readability, and the meaning -- arguably the most important part of the name -- is still vague.

Examples:

One of the things that source code obfuscaters do is rename variables and function calls to remove their meaning. Why? Because it's much harder to understand code that has no context. Consider the following snippets of Javascript:

Original code:

This is fairly useless code, does nothing too important, but even without comments (which I always encourage), you can easily follow along to figure out what the code is intended to do.

var mass, acceleration;
var force = 0;
var isAccelerating = false;

mass = 10;
acceleration = 9.8;

function calculateForce(massKg, accelMeterSecondSq) {
    return massKg * accelMeterSecondSq;
}

isAccelerating = (acceleration > 0) ? true : false;

if (isAccelerating) {
    force = calculateForce(mass, acceleration);
}

console.log('Value is: ' + force);

Even if you're unfamiliar with the formula for force ( force = mass * acceleration), you could follow along and get the meaning of code.

Obfuscated:

Here's what an obfuscater might turn the original code into. I've kept the formatting the same, so we can just focus on the differences in the identifier names.

var b,c;
var d = 0;
var t = false;

b = 10;
c = 9.8;

function a(e,f) {
    return e*f;
}

t = (c > 0) ? true : false;

if (t) {
    d = a(b,c);
}

console.log('Value is: ' + d);

While functionally the program does the same thing, unless you happen to recognize numbers like 9.8 as the acceleration due to gravity and that force = mass * acceleration, you would have a very hard time placing the code in any sort of context.

Stupidly named identifiers:

I've seen far too much of this kind of thing. To the programmer who wrote this code, it makes perfect sense, but to anyone else it would be hard to follow.

var heavy, fast;
var hard = 0;
var acceleration =  false;

heavy = 10;
fast = 9.8;

function calc(val1, val2) {
    return val1 * val2;
}

acceleration = (fast > 0) ? true : false;

if (acceleration) {
    hard = calc(heavy, fast);
}

console.log('Value is: ' + hard);

I consider this version of the code worse than the obfuscated code. At first glance, the identifiers appear to provide contextual information, but as you read the code, you discover that they are named in such a way as to inhibit understanding.

Anecdotal proof

An excellent programmer and mentor of mine told me a story that illustrated very clearly the effect that identifier names have in software development. He was in a job where he was treated poorly and had decided to leave. In his last week of employment he altered his source code as a final act of rebellion. He didn't add a back door or a virus which could have gotten him into legal trouble. Instead, he changed all the identifier names. isReady() became isNotDoneNot(), correct became unbadly, date became value123. Unfortunately, the programmer who inherited the code was a good friend of my mentor and called him up bitching about how my mentor had ruined his life because the code was impossible to follow. They're still friends.

Summary

Please use logical, descriptive names for your identifiers. Your fellow programmers will enjoy working on your code, even if they aren't aware why. This also applies to Lone Wolf programmers as well who "work alone, damnit!" Descriptively named identifiers will greatly reduce the amount of time you spend saying to yourself "WTF was I trying to do here?!" when maintaining your old code.

Repair: 30 year-old coffee maker

Intro

About 15 years ago, I inherited a vintage Black and Decker thermal carafe drip coffee maker, model TCM-411 from the 80's. It was given to me in payment for helping ready an apartment for sale.

I really like the TCM-411. It's very simple to operate (no timers or clocks to mess with), it makes good hot coffee, and the glass-lined, mirrored thermal carafe keeps the coffee hot for hours. The only control is a slide switch on the front that turns on the brewing process and which automatically turns it off once all the water has been drained from the reservoir tank. (PDF manual, Manuals Site)

MY BLACK & DECKER TCM-411 IN ALL ITS TIME-STAINED GLORY

And then one day...without  warning..it stopped working! Almost entirely, but not quite. I could get it to brew coffee if I fiddled with it for 2-3 minutes.

This post is the story of how, through trial and error, I eventually restored this fantastic coffee maker to its former glory and fully expect to brew another 30 years of great coffee with it. 

TL;DR

After much investigation and final confirmation by my friend Josh of what I suspected was the cause of my coffee maker's problems, I learned a valuable lesson:

Electrolytic capacitors eventually go bad

If the device that you are attempting to repair has electrolytic capacitors, and the device is more than, say, 10 years old, the capacitors are highly likely to be the cause of your problems.

The Full Deal

I'm no electronics expert -- more of an electronics dabbler. It's something that has always interested me and I've read many books on the subject, know enough to make mistakes, had conversations with actual experts and have started and sometimes completed many simple electronics projects.

My goal in this post is to describe the thought processes I went through when investigating what was wrong with my coffee maker and trying to repair it. 

How coffeemakers work

All coffee makers basically have the same parts with some variations:

  • Heating Element: A sealed resistor that heats the water.
  • Thermostat: Device that shuts off the heating element once it reaches a threshold temperature.
  • Temperature Fuse: To prevent coffee makers from causing fires if they malfunction, they all contain fuses that flow when a threshold temperature is reached.
  • Water Reservoir: Holds the water.
  • Ground Coffee Holder: This is the place where you put the ground coffee beans.
  • Coffee Pot: Container to hold finished coffee.

The heated water flows over or through the ground coffee beans and into the coffee pot.

What went wrong with my coffee maker

When it's functioning normally, the TCM-411 coffeemaker is simple to use. After filling the water reservoir and adding ground coffee to the basket filter you start the brewing process by sliding the switch on the front on the coffeemaker to the right. A red LED next to the switch lights up and stays on. About two seconds later you hear "blurp" as the first bit of water is heated and rises up from the heating element and trickles over the ground coffee.

One day, when I slid the switch over to start brewing my coffee and it didn't "catch". The red LED went on for a moment and then shut off. After some experimentation, I realized that I could get the coffee maker to complete a brew cycle if I kept sliding the switch over, and as soon as it turned off, I would slide it over again. If I did that for around 3 minutes it would finally catch and remain on through the rest of the brew cycle.

I had two theories as to why the coffee maker wasn't working:

  • Maybe the water reservoir outflow is plugged up? This was my first theory. I thought water wasn't getting into the heater, causing the heater to shut off by whatever mechanism that made it shut off during normal operation. I realized that this theory was incorrect once I figured out that fiddling with the sliding switch would eventually lead to a complete brew cycle.
  • Maybe the thermostat is broken? This was my second theory, and I was convinced I was right (oh, but I wasn't). It just seemed right that a broken thermostat would cause the coffee machine shut off prematurely.

Disassembly and Investigation

I took apart the TCM-411 to see if a visual inspection of its inner workings would lead to a diagnosis of why my favorite coffee maker wasn't working.

I learned a lot from the disassembly. However, while I'm presenting the information in a nice bulleted list, like it was obvious to me, it took me some time to figure out what all the parts were and what role they played.

For example, while I had read about thermistors, I had never actually seen one in use. The one in the coffee maker looked like a glass diode to me. Also, when I first came upon the two temperature fuses (you'll see them in the picture gallery) I also thought they were diodes and was puzzled as to why there two in series. To figure out what the parts were, I took educated guesses (that looks like a diode, that looks like a resistor, etc) and Googled part numbers and images with which to compare. 

  • Rather than a thermostat, the TCM-411 uses a thermistor to cut power to the heating element. A thermistor is a resistor that reacts to temperature. A PTC (positive temperature coefficient) thermistor increases in resistance as it gets warmer. An NTC (negative temperature coefficient) thermistor -- which is the kind my coffee maker has -- goes down in resistance as its temperature increases. I tested the thermistor and confirmed it was working correctly.
  • Coffee makers have thermal fuses or cutoff to prevent fires. If the temperature goes beyond a certain point (in this case 240 degrees Fahrenheit) the fuse will blow and instantly cut current to the heating element. The reason there are two fuses, when one would suffice, is that the fuses can go bad and it's much cheaper to add a second fuse than defend lawsuits. The only test you can make on a thermal fuse, as far as I know, is to make sure that it doesn't have a short -- that it allows electricity to flow through it.
  • The printed circuit board that provide the single control for the coffee maker has a momentary sliding switch, a relay, some resistors and a couple of electrolytic capacitors. The capacitors were labeled C0 (20uf) and C1 (44uf). Everything checked out ok with the switch and relay, but the capacitors looked suspicious. There was residue around them. When I mentioned this to my oft-consulted friend Josh, he immediately said: "That's it. Electrolytic capacitors are cheap and always go bad." He explained that electrolytic capacitors are really cheaply made -- they basically contain paper and an electrolyte which over time will leak out of the capacitor.

Repair

Once I understood that the electrolytic capacitors were bad, the repair was really easy. I didn't have the exact capacity capacitors I needed (a 50uf and a 20uf) but Josh showed me that we could use capacitors in series and parallel to get whatever values we needed. We used two 100uf capacitors in series to replace the 50uf capacitors, and two 10uf capacitors in parallel to replace the 20uf capacitor.

Once we replaced the capacitors, my TCM-411 started brewing again. It was a happy day.