Jim Lynch Codes
  • Blog
  • About Jim

Writings about one coder's stories & experiences.

A TDD With TypeScript Setup Guide

7/25/2019

0 Comments

 
Recently I've been getting back into doing some algorithms coding challenge problems, and I really wanted to setup for myself a nice, comfortable coding environment that I could be proud of and that really followed the TDD principles of Uncle Ben, Ken Beck, and all the other gurus. Anyway, this is a guide for setting up simple, barebones TypeScript node.js projects for TDD. I'll show you how to set up a brand new node project, how to make your project a "typescript project", how to add mocha and chai in watch mode, and finally how to see your test results in a nice code coverage report. It's a lot I know, so let's dive into it!

A Totally Fresh Project

I'm going to assume you're using a mac terminal or at least some type of unix-based shell. Now, let's make a completely new, fresh project- no scaffolding, no starter boilerplate crutches- just a straight up empty folder! 
mkdir TypeScript-TDD-Starter

Use Node v10 (For This Tutorial)

I can't gaurentee the commands here will work for you if you are on a different node verison. At the time of this writing I'm using node v10.16.0, but any v10 version of node should be fine. I normally like to adjust my node version with nvm like so:
nvm i v10
nvm use v10

Make It An Npm Project

Ok, now navigate into your empty project folder, and let's make this thing a node project! We''ll create a package.json file with the npm init command. You can pretty much just keep hitting enter and accept the defaults for the questions it asks as you can always go in and change things in you package.json later.
npm init

Make It A TypeScript Project

Now, let's turn this plain jane node project into a typescript project! First, let's install typescript globally as this will give us access to the typescript command line tool, tsc.
npm i typescript -g
We can then use tsc to scaffold out a tsconfig.json file. This file should live in the root directory of your project. It controls things like the typescript source maps, how the typescript is compiled down to javascript, etc. Also, later when we talk about ts-mocha you will see that we need this tsconfig.json file to pass as an argument to ts-mocha. Anyway, run tsc with the --init flag to generate the config file.
tsc --init
It's kind of annoying though- the generated file contains a lot of JavaScript comments, but json technically does not support comments. This means then that you need do decide there on the spot what options you want to hack away forever and what things you would like to uncomment and set live... To be honest, you can really just ignore the whole generating a tsconfig.json file thing and just create one yourself with contents that looks like the code below:
​tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "esModuleInterop": true,
     "outDir": "dist",
    "sourceMap": true
  }
}
Now we just need to make some updates to the package.json file. Add these two commands in the "scripts" section for start and test. In this case npm start runs the tests in "watch mode" so that the tests are rerun after any change of the code while npm test does a single run of the tests and produces code coverage reports (since we use using both "text-summary" and "html" then it will output the coverage results to the console and create an entire little website displaying the code coverage! The big block of config settings under "nyc" controls how nyc runs your tests files. Here we are basically just telling it to supports typescript and specify what resporter(s) we want it to use.
 package.json
{
  "name": "typescript-tdd-starter",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "./node_modules/ts-mocha/bin/ts-mocha -p ./tsconfig.json ./**/*.test.ts -w --watch-extensions ts",
    "test": "nyc mocha '**/*.test.ts'"
  },
  "author": "Jim",
  "license": "MIT",
  "nyc": {
    "extension": [
      ".ts",
      ".tsx"
    ],
    "require": [
      "ts-node/register"
    ],
    "reporter": [
      "text-summary",
      "html"
    ],
    "all": true,
    "sourceMap": true,
    "instrument": true
  }
}

Install Things!

Ok, there are just a few npm libraries that we need to install in order to be able to run our TDD tests. Mocha is basically the gold standard for unit testing backend javascript, and chai gives us the excellent "expect ... to" syntax that makes our unit tests more readable, understandable, fuzzy, and nice. We also install ts-mocha as that's used to run the tests in "watch mode", and for some reason even though I had typescript installed globally my machine still complained that it could not find it so I needed to add typescript as a dev dependency as well. 
npm i chai -D
npm i mocha -D
npm i @types/chai -D
npm i @types/mocha -D
npm i ts-mocha -D
npm i typescript -D
npm i nyc -D

Make A Basic TypeScript Class With Some Calculation Function

Ok, now that all the boring setup that needs to be done in order to get this TDD show on the road in out of the way, let's make a simple class! Here I'll name the class something generic but similar to how I've been naming previous ones. How about ChallengeProblemSolver? For classes that are doing some calculation or providing some helper function I like to name them with an -er suffix. To me, this makes sense. The functions are the things that are ultimate doing the work, and the class is a container. It's a noun, a thing, even you could think of the class as a person if you really wanted to personify it. The class is the container of one or a few functions, and I think also naming the class this way enforces the "single responsibility rule". Your classes should each be doing one thing and doing that thing well, and so it shouldn't be very difficult for each class to say, "this is what I do. This is who I am, and this is my name".
ChallengeProblemSolver.ts
export class ChallengeProblemSolver {

    solveProblem( input : Array<number> ) {

        return 4;
    } 

}
The class above exposes one public function, solve problem, which takes an array of numbers and returns the integer 4. I'm not sure what problem is being solved here that the answer is always 4, but this is a great example of a simple TypeScript class with a single function.

Make A Test For This Class And Function

Now comes the fun part, writing tests for the class! Let's create a new file called ChallengerProblemSolver.test.ts and put it next to the other files. We'll import our class that we want to test, import "expect" so that we have it to use in our assertions, and then make the overarching describe block for our tests of this solveProblem function. We declare a variable called challengeProblemSolver outside of our beforeEach or it blocks purposely so that it can be used across all of them. Before each it block the beforeEach is run thus creating a fresh instance of ChallengeProblemSolver to be used in each test. Then inside of the it block we invoke the function and use the "expect" keyword to assert that the result is the expected outcome.
import { ChallengeProblemSolver } from './ChallengeProblemSolver';
import { expect } from 'chai';

describe('ChallengeProblemSolver.solveProblem', () => {

    it('should return 4', () => {
        const challengeProblemSolver = new ChallengeProblemSolver();
        expect(challengeProblemSolver.solveProblem([1, 2, 3])).to.equal(4)
    })

})
So now we have a class, a test for it, and all the npm setup things out of the way, awesome! Now all that's left to do is run the tests and get to TDD developing!

Two Ways To Run It- TDD Mode & Code Coverage Mode

Notice how there are two scripts in my package.json file above: start and test. These are pretty common npm commands, but the way that I'm using them here is a bit unusual. Let's take a closer look at them. When you run npm start then that will trigger this command:
./node_modules/ts-mocha/bin/ts-mocha -p ./tsconfig.json ./**/*.test.ts -w --watch-extensions ts
So what's happening in the above command? Well, first we are running "ts-mocha", basically mocha but for typscript. We are passing it the -p flag, short for project, which takes our tsconfig.json file as an argument, and then finally we are telling it run any file anywhere within the current directory that ends in ".test.ts". We are then adding the -w flag for "watch" to make the tests rerun automatically when the source code or test code is changed (essential for tdd!), and we also add the "--watch-extensions ts" to make sure the watch mode works with our typescript files. Note that you could also install ts-mocha globally and avoid having to drill into the node_modules, but this way in my experience seems to cause less troubles. Now, let's take a look at the command for ​npm test:
nyc mocha '**/*.test.ts'
In this above command we are using "nyc" along with mocha to run the tests and generate a code coverage report. We pass in one argument to denote that any file in the current directory that ends in .test.ts should be treated as a test file. Back when we created the package.json file we added a block for "nyc", and that is where we specify that we are using typescript and control a few other settings.

Now, Be Disciplined And Write Tests For Your Code!

Writing good unit tests can seem tricky at first, but it is something that anyone can learn. Practice, practice, practice, and over time you will start to see patterns and discovers things that work or don't work for you. TypeScript and testing can be frustrating things to set up correctly and will give your even more things to debug! For some individuals / teams the fear not being able to write tests or not benefitting enough from having tests is so strong that it prevents them from even giving it a chance at all! The client, product owner, and end users do not care at all about neither typescript nor unit testing, but that doesn't mean they aren't important! These are things that never make it to the visible surface but that help you to build better things faster. Unit tests are there solely for you, the developer. They are there to give you confidence that your functions actually work the way you intended them to, and they provide a nearly instantaneous means of reaffirming that confidence at any time in the future. They are there to expose the bugs you would have otherwise had, and do prevent them from appearing after the addition of new features. Tests and TypeScript are there to make you life better, and you should embrace them. I hope this guide will help you get over the hurdles of just setting up a development environment so that you focus on actually harness this test-driven workflow to build some amazing new software.
0 Comments

Your comment will be posted after it is approved.


Leave a Reply.

    ​Author

    Picture
    The posts on this site are written and maintained by Jim Lynch. About Jim...
    Follow @JimLynchCodes
    Follow @JimLynchCodes

    Want FREE access to
    my daily stock tips 
    ​newsletter called,
    "The Triple Gainers"?

    Sign up here!

    Categories

    All
    Actionscript 3
    Angular
    AngularJS
    Automated Testing
    AWS Lambda
    Behavior Driven Development
    Blogging
    Business Building
    C#
    C / C++
    ClojureScript / Clojure
    Coding
    Community Service
    CS Philosophy
    Css / Scss
    Dev Ops
    Firebase
    Fitness
    Flash
    Front End
    Functional Programming
    Git
    Go Lang
    Haskell
    Illustrations
    Java
    Javascript
    Lean
    Life
    Linux
    Logic Pro
    Music
    Node.js
    Planning
    Productivity
    Professionalism
    Python
    React
    Redux / Ngrx
    Refactoring
    Reusable Components
    Rust
    Security
    Serverless
    Shell Scripting
    Swift
    Test Driven Development
    Things
    TypeScript
    Useful Sites
    Useful Tools
    Video
    Website Development
    WebStorm
    Writing

    Archives

    October 2020
    September 2020
    May 2020
    April 2020
    February 2020
    January 2020
    December 2019
    October 2019
    September 2019
    August 2019
    July 2019
    June 2019
    May 2019
    April 2019
    March 2019
    February 2019
    January 2019
    December 2018
    November 2018
    October 2018
    September 2018
    August 2018
    June 2018
    May 2018
    April 2018
    March 2018
    February 2018
    January 2018
    December 2017
    November 2017
    October 2017
    September 2017
    August 2017
    July 2017
    May 2017
    April 2017
    March 2017
    February 2017
    January 2017
    December 2016
    November 2016
    October 2016
    September 2016
    August 2016
    July 2016
    June 2016
    May 2016
    April 2016
    March 2016
    February 2016
    January 2016
    December 2015
    November 2015
    October 2015

    RSS Feed

  • Blog
  • About Jim
JimLynchCodes © 2021