Repair: Bad Electrolytic Capacitor Strikes Again, This Time In A Refrigerator Control Unit

TLDR;

My Kitchen Aid model KBLC36FMS02 built-in refrigerator had been making odd sounds for a few weeks and then one day stopped cooling. I pulled the fridge out of its niche and eventually pulled out the control board on top of condenser coil. When I Googled some of the numbers on the PCB (ex: "W10219463 control board fix"), I came across the blog of Steve Jenkins, who had an excellent post on the control board issue with all the information I needed to repair it. The problem with my control board came down to a faulty 220uf electrolytic capacitor. And as I learned from my friend Josh when I repaired a 30-year old coffee maker, electrolytic capacitors always go bad.

Details

When my refrigerator, a Kitchen Aid model KBLC36FMS02 started making odd sounds, I ignored it. A few weeks later, when it stopped cooling, I couldn't ignore it.

My first thought was that the compressor had a leaked coolant and no longer worked. I pulled the refrigerator out of its niche and took apart the enclosure on top of the fridge which housed the compressor and condenser coil.

Before taking apart the top housing, I had unplugged the refrigerator. Since I didn't see anything obviously wrong with the compressor, I plugged the fridge back in to see if that might reset the temperature sensors. The compressor started up and I saw frost appear on the output tube, so I guessed that the compressor was working correctly.

Next I took a look at the condenser coils. I thought that maybe the refrigerator had turned itself off because, if the coils were sufficiently dirty, the fridge couldn't release heat. The coils were filthy, having never been cleaned in the 10 years since the fridge had been in operation.

Dirty condenser coils

I cleaned out the coils and started up the fridge again, but it was clear after a few minutes that it still wasn't cooling. That's when I noticed that the condenser coil fan wasn't spinning. So, my next guess as to what was wrong with the fridge was that the control board, which I could see above the condenser coils, had a problem with it. I removed the board and took a look at it.

From my experience fixing a 30-year old coffee maker I learned that electrolytic capacitors often go bad. However, the capacitors on the board didn't look damaged or have any discharge like the ones I replaced in my old coffee maker.

I Googled the numbers on the PCB of the control board, hoping to find if I could find a replacement board and how much it would cost. Ebay had several services that would either sell you a new board ($850!!) or repair existing boards ($70-$200).

Then I came across Steve Jenkins' blog which had a post about the very control unit I was researching. Steve's post is excellent, with lots of useful information, including the fact that the failure condition I was experiencing is a well-known issue with the control board (model W10219463), and that two parts in particular fail on the board: a relay and an electrolytic capacitor.

From Steve's blog I learned that the single 200uf capacitor on the board is the one to go bad and when I looked at it again, I noticed that even though there was no discharge from the capacitor, its top was bulging, just like Steve described.

Bulging capacitor top

I removed the bad capacitor and tested it, confirming that it did not hold anywhere near the charge it was supposed to. 

Low capacity on bad capacitor

So, I replaced the bad 220uf capacitor with a fresh one, reinstalled the control board and started up the refrigerator. The compressor went on as did the fan! After a few minutes I confirmed that the fridge was cooling again. 

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 you're 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.