The Blog of Brandon G Parker

Introduction to Unit Testing es6 JavaScript Modules

In this post I will demonstrate how to go about unit testing the most basic example of JavaScript code. You’ll quickly see how important unit testing can be and how much it can introduce a very solid quality check against your code, allowing you to release more confidently.

To start, let’s assume the following very basic example. Note that this example is so basic you’d probably never have code like this.

string-functions.js

export function convertToLowerCase(str) {
     return str.toLowerCase();
}

This exported function, when called, will take in a string and return the string in all lowercase. Now let’s consider writing some unit tests for the code as-is. We can check to see the function actually exists. This is the most basic check and while it doesn’t provide any actual code coverage it will add a quality check to make sure if the function is removed the test will not pass.

For all of these examples we’re assuming that we’re running karma, mocha, sinon, and chai for our technology stack (although other packages will work, too). We’re also going to assume that these are wrapped in the required describe wrapper, so this is just the it statement for each test.

it('convertToLowerCase should be a function', function() {
     assert.isFunction(strFunctions.convertToLowerCase);
});

As I previously stated this provides no coverage and we actually have 0% code coverage at this point. But before we can write more tests we probably need to ask some questions about the code in order to know what tests to write. So let’s ask some questions about this code:

  1. Is passing a string required?
  2. What happens if something other than a string is passed?
  3. Is a string always returned?
  4. Does the function return a lowercase version of the string that was passed?

Let’s look at these one at a time.

Is passing a string required?
Yes, in order to use the toLowerCase() method a string needs to be passed.

What happens if something other than a string is passed?
Passing something like an object is going to throw an error. Passing nothing will cause an error. We really only want to be able to pass a string to this function.

Is a string always returned?
For sure if a string is passed in a string will be passed out. But if we’re not passing a string in we have unexpected results. We always need a string.

Does the function return a lowercase version of the string that was passed?
We need to assume a string is being passed, it’s important to make sure the expected result is the actual result of this function so that we can possibly iterate on it later.

Based on these answers we need to modify the code some in order to ensure expected results always.

export function convertToLowerCase(str) {
     if (!str || typeof str !== 'string') {
          return false;
     }

     return str.toLowerCase();
}

Now, with these changes, we force the developer to pass in a string. We won’t get any sorts of errors if we don’t because we aren’t executing that code. We can also then guarantee that we’re always going to return a string and the string will always be as expected. So let’s write tests to confirm this:

it('convertToLowerCase() should return bool false when no parameter is passed', function() {
     assert.isBoolean(strFunctions.convertToLowerCase());
     assert.equal(false, strFunctions.convertToLowerCase());
});
it('convertToLowerCase() should return bool false when parameter passed is not a string', function() {
     assert.isBoolean(strFunctions.convertToLowerCase(1234));
     assert.equal(false, strFunctions.convertToLowerCase(1234));

     assert.isBoolean(strFunctions.convertToLowerCase(['asdf']));
     assert.equal(false, strFunctions.convertToLowerCase(['asdf']));

     assert.isBoolean(strFunctions.convertToLowerCase(true));
     assert.equal(false, strFunctions.convertToLowerCase(true));

     assert.isBoolean(strFunctions.convertToLowerCase(null));
     assert.equal(false, strFunctions.convertToLowerCase(null));

     assert.isBoolean(strFunctions.convertToLowerCase({ 'option': true }));
     assert.equal(false, strFunctions.convertToLowerCase({ 'option': true }));
});
it('convertToLowerCase() should returna  string when a string is passed', function() {
     assert.isString(strFunctions.convertToLowerCase('ASDF'));
});
it('convertToLowerCase() should return a lowercase string when a string is passed', function() {
     assert.equal('asdf', strFunctions.convertToLowerCase('ASDF'));
     assert.equal('asdf', strFunctions.convertToLowerCase('asdf'));
     assert.equal('another test', strFunctions.convertToLowerCase('Another Test'));
});

Before we refactored the code we had no branches but 1 line, 1 statement, and 1 function, all for 0% code coverage. When we refactored the code we introduced 4 branches and an additional line and statement. We’ve successfully tested all lines, statements, functions, and branches. We’ve introduced negative checks to go along with our happy-path tests and have much greater confidence in the future that if we make a change to this function we’ll know before we release it if it might break a function that relies upon it.

Pin It
Read More Leave comment

Code Coverage with es6, Babel, Karma, Mocha, and Webpack

I was trying to get JavaScript unit testing set up at work and was really struggling. While I could get unit tests and established a testing framework I wasn’t able to determine my code coverage. For those of you that haven’t tested before, code coverage is a really important aspect of unit tests because it will tell you where you have gaps in your tests. If you’re not testing a specific branch or function you have a greater chance of pushing out a bug because the code isn’t tested.  So code coverage is pretty important.

Take this piece of my devDependencies within my package.json.

package.json:

"devDependencies": { 
    "babel-core": "6.23.1", 
    "babel-loader": "6.3.2", 
    "babel-preset-es2015": "6.22.0", 
    "babel-preset-stage-1": "6.22.0", 
    "chai": "3.5.0", 
    "gulp-mocha": "4.0.1", 
    "karma": "1.5.0", 
    "karma-cli": "1.0.1", 
    "karma-coverage": "1.1.1", 
    "karma-mocha": "1.3.0", 
    "karma-phantomjs-launcher": "1.0.2", 
    "karma-sinon-chai": "1.2.4", 
    "karma-webpack": "2.0.2", 
    "mocha": "3.2.0", 
    "sinon": "1.17.7", 
    "sinon-chai": "2.8.0", 
    "webpack": "2.2.1" 
} 

This is a pretty standard build out when you’re using es6 (Babel) and are transpiling down from 6 to 5 as part of your bundling/minifying/uglifying process (Webpack). I chose the Karma/Mocha/Sinon/Chai technology stack simply because it’s widely adopted and lightweight. Some people prefer Jasmine more, which is fine, Jasmine comes with a lot of this functionality built-in, but I like to have options.

A lot of sites tell you to set up your karma.conf.js file something like this:

karma.conf,js


const src = './scripts/**/*.js',
      tests = './tests/**/*.spec.js';

const karmaConfig = {
    frameworks: ['mocha', 'sinon-chai'],
    files: [
        src,
        tests
    ],
    preprocessors: {
        src: ['webpack', 'coverage'],
        tests: ['webpack']
    },
    webpack: webpackConfig('test'),
    reporters: ['coverage'],
    browsers: ['PhantomJS'],
    client: {
        captureConsole: false
    },
    specReporter: {
        showSpecTiming: true
    },
    reportSlowerThan: 25,
    coverageReporter: {
        dir: 'coverage',
        reporters: [
            { type: 'text' },
            { type: 'text-summary' },
            { type: 'html' }
        ]
    }
};

karmaConfig.preprocessors[src] = ['webpack', 'coverage'];
karmaConfig.preprocessors[tests] = ['webpack'];

module.exports = function (config) {
    config.set(karmaConfig);
}

If you have something like this you’ll get bundled output and your tests will run, but your test coverage will be extremely low. If you add the coverage reporter to the tests you’ll likely get an error saying you can’t require the module because the module is already required. You’ve created a cyclical reference.

What we want really is to run the tests, and from the tests determine what source code was executed by the tests. But we also need to run the tests and code coverage on the non-transpiled code. In order to do this we need to include another handy babel package:

npm install babel-plugin-istanbul --save-dev

Istanbul is pretty much the gold standard when it comes to coverage reporting in the front-end world and it does this extremely well. I also have a JSON file of config settings, that currently has this node for Babel:

config.json


"babel": {
     "presets": [
          ["es2015", {
               "modules": false
          }],
          "stage-1"
     ]
}

So I need to add another configuration to this file:


"babel": {
     "presets": [
          ["es2015", {
               "modules": false
          }],
          "stage-1"
     ],
     "plugins": [
          ["istanbul", {
               "exclude": [
                    "**/*.spec.js"
                ]
          }]
     ]
}

You’ll notice a line about exclusions. This plugin by default will exclude certain naming conventions from your code coverage. If you don’t use one of the standards (which sometimes I don’t) it will include the test code files as part of your code coverage report – not good!  So I just add in the path to my test files to prevent this.

We also need to remove the coverage reporter from the files but leave the coverage reporter settings. This seems wrong but I promise this works. We also don’t want to include source files in our karma.conf.js file because right now we only want to get coverage for files that we have created a test file for. Why? Well what if we had a vendor script? If we included that file we’d be penalized for not writing unit tests on the file or maybe we’d write unit tests on the file anyway, which could be troublesome if it’s not written for testability. Ideally third-party and vendor scripts have been tested during the development phase.


const tests = './tests/**/*.spec.js';

const karmaConfig = {
    frameworks: ['mocha', 'sinon-chai'],
    files: [
        tests
    ],
    preprocessors: {
        tests: ['webpack']
    },
    webpack: webpackConfig('test'),
    reporters: ['coverage'],
    browsers: ['PhantomJS'],
    client: {
        captureConsole: false
    },
    specReporter: {
        showSpecTiming: true
    },
    reportSlowerThan: 25,
    coverageReporter: {
        dir: 'coverage',
        reporters: [
            { type: 'text' },
            { type: 'text-summary' },
            { type: 'html' }
        ]
    }
};

karmaConfig.preprocessors[tests] = ['webpack'];

module.exports = function (config) {
    config.set(karmaConfig);
}

Now when we run our tests we will get coverage only on the files that we want to have coverage for. Look out for posts on how to write unit tests and how to write your code to be testable in the future. There will also be a post on how to test third-party scripts, because sometimes it’s a critical component and you need the certainty that upgrades won’t break functionality on the site.

Pin It
Read More Leave comment

Simple NodeJS Web Server

I’m working on a personal project where I need to spin up a simple web server to serve up some deployable assets. In order to get a low overhead server I’m looking into a NodeJS web server.  So I set out to create one. This is the first, and simplest, one I could fine. It gets the job done but I think I probably need something more feature rich. This is as bare bones as it gets.  Let’s dig into this.

package.json:

"dependencies": {
    "connect": "^3.5.0",
    "serve-static": "^1.11.2"
}

This web server, outside of Node, only requires two packages: connect and serve-static. Then, I create a server.js file with the following code:

const connect = require('connect'),
      serveStatic = require('serve-static');

const port = '8080';
const location = './dist';

connect().use(serveStatic(location)).listen(port, function() {
    console.info('Server running on http://localhost:'+port+'...');
});

In a command prompt that’s navigated to the location where this code is located, type node server.js and it starts the web browser.

This initializes a simple web server at http://localhost:8080 and then loads up anything loaded in the location variable. It works, but doesn’t really have any features other than this. So I’ll be looking for a second version of this, but wanted to share this one as it’s super simple and in a pinch can be used to spin up the most basic of web servers.

Pin It
Read More Leave comment

Acorns: Small Investments in Your Future

Acorns: Small Investments in Your Future

Last year in December (of 2015) I enrolled in a little-known application called Acorns. This company would look into your transaction history; all of your credit card and other purchases, and then round up to the nearest dollar from the purchase and take the difference. They’d these small amounts of money up, anywhere from 1 cent to a dollar, and once you had $5 available, would invest these micro-investments into a series of mutual funds.

 

I didn’t expect much out of this since we’re talking literally pennies per purchase. But I was wrong. Site after site emphasizes the importance of investing whatever you have to work with. So while I invest already in a number of different ways, I decided to give this a try. From $5 onward my balance slowly started to grow. Eventually I started to see returns outside of my investments as dividends were given and funds sold. I don’t ever expect this to become a nest egg for me, but I do use it for an emergency fund. I slowly invest as I make additional purchases, and it acts basically as a savings account earning around 3% for me. If I had an unexpected expense like maybe one of my cats got sick, I’d just withdrawal from the account and be on my way.

Normally you can get $5 when you sign up but they seem to be running a special where you will get $10 free for signing up. Check it out here.

 

Pin It
Read More Leave comment

2016 Financial Year in Review

As I normally do at the beginning of each year is to reflect on how things went in the previous year. Topics are a general reflection, financial, and fitness. This is the first of these posts.

Dining

In general I spent 6.90% less this year on dining than I did last year. However, I am spending too much of my dining budget (and exceeding my planned budget for dining) the majority of months. The top three categories: dinner, lunch, and alcohol, I have a lot of influence over. Instead of ordering out or dining out for dinner, cook food. Instead of going out for lunch every day at work, bring a lunch a few days a week. As for alcohol some of this will drop as dinner dining out goes down, but also spend less time drinking, reserve it to not drinking during the week, or perhaps go on discount days like Goose Island does.

2016 dining breakdown by category

My analysis has shown that even though I eat out more often the overall cost of the meals are less. I believe I have identified the reason because at my last company we would grab drinks during lunch or after work pretty often. I don’t do that now. This would account for a large amount of the nearly 7% decrease in overall spend.

Groceries

Ideally I wouldn’t spend as much on groceries as I do dining but also that groceries last longer and make more meals. This is true for 2016, but I’m spending more, 17.5% more, on groceries this year than last. Additionally, as seen in the graph, the “Other” category is the largest group, which means that I need to determine additional categories to track against.

2016 groceries by type

Meats are by far the most expensive, so reduction in meats will result in less cost as well as healthier eating. Generally though I need to plan out my meals better to make grocery shopping more efficient. It’s also important to note that just because I don’t spend a lot of my overall grocery spend on fruits and vegetables doesn’t mean I don’t buy them often. These are normally much cheaper than meats and therefore don’t contribute to a lot of my overall spend. I have entire grocery visits of just vegetables and fruits.

Additionally, the $35 a week experiment that I did a few years back seemed to work pretty well. So if I up that to say $50 a week so that I can get leftovers enough to also bring lunches, this should influence both budgets in a positive way.

Travel

Did I travel more in 2016? Well, I don’t think so, but I did spend more on travel overall, nearly 53% more to be exact. Where did the increases come from? I did take a few trips this year but didn’t rent a car or have large expenses with them. I had a nearly 38% increase in airfare costs this year. This could be caused by not having the points budget at the time of travel to not have to pay for tickets. Airfare is also increasing in cost.

I started spending on tolls this year whereas I didn’t at all last year. This is caused by having a job that requires me to travel to suburban areas of Chicago now and there are tolls on the roads. With this change, I also recorded a 171% increase in gas spending this year. Again, this is due to having to drive to the suburbs for work.

Financial Health

Spending doesn’t really give a good picture of overall financial health as this category does. I’ve broken this down into four categories: saving contributions, investment contributions, retirement account worth, and overall net worth.

This year I recorded a 58% increase in my savings contributions. This means money I put into saving accounts increased by 58% in 2016 over 2015. It’s a pretty large amount and this year I’ll probably reduce that some to allow more to go into investments instead, where I can potentially get a higher return.

As for overall investments (which are contributions made to investment accounts – not retirement or 401(k) or the like) I increased overall contributions to these accounts nearly 47%. This doesn’t reflect a 47% increase in net worth, per se, just that I contributed 47% more to them. I didn’t calculate overall net worth change for these accounts.

Retirement account contributions (things like IRA contributions and 401(k)) also rose in 2016 by 26%. Again, this does not reflect overall increase in value, just contributions. I didn’t calculate value change, but I do know it’s higher, not as high as 26% but I did have a very good year and is in the double digits.

Lastly, and probably the most telling metric, is net worth. Take all your account balances and assets, subtract the debts and liabilities, and you get a value. That’s your net worth. This year was a good year for debts and liabilities for me as I finished paying off all of my student loans in December of 2015, five years ahead of schedule. Therefore this is my first year completely debt free. Overall my net worth increased nearly 5.5% in 2016. While I’m quite proud of that number I hope to increase it with smart investing and savings and reducing overall spending.

Pin It
Read More Leave comment