dev javascript, tdd, unit_testing

As applications get more complex, they come with more dependencies. Mocking helps us isolate the subject under test so that our unit tests can run any external dependencies. In this example, I’d like to show a simple mocking example using Jasmine and Karma in an Angular app.

Case study: Mocking a HTTP dependency

In this example the service I want to test is called TldService. It basically gets a raw HTML from a URL and extracts some data using regex.

It looks something like this

export class TldService {

  constructor(private fetchHtmlService: FetchHtmlService) { }

  getAllSupportedTlds(): string[] {
    const rawHtml = this.fetchHtmlService.getRawHtml();
    const supportedTlds = this.parseHtml(rawHtml);
    return supportedTlds;
  }

  private parseHtml(html: string): string[] {
    const pattern = /some-regex-pattern/g;
    const regEx = new RegExp(pattern);
    let tldList = [];

    let match = regEx.exec(html);
    console.log(match);

    while (match !== null) {
      tldList.push(match[1]);
      match = regEx.exec(html);
    }

    return tldList;
  }
}

This service depends on FetchHtmlService which does the getting HTML part. Nice thing about injecting this dependency is that we can replace it with a fake one while testing. This way we can test it without even having to implement the dependency.

import { TestBed } from '@angular/core/testing';
import { TldService } from './tld.service';
import { FetchHtmlService } from './fetch-html.service';

describe('TldService', () => {
  beforeEach(() => TestBed.configureTestingModule({

  }));

  it('should be created', () => {
    const service: TldService = TestBed.get(TldService);
    expect(service).toBeTruthy();
  });

  it('should parse html and extract TLD list', () => {
    const fetchHtmlService: FetchHtmlService = new FetchHtmlService();
    spyOn(fetchHtmlService, 'getRawHtml').and.returnValue('<a href="registrar-tld-list.html#academy">.academy</a>');
    const service: TldService = new TldService(fetchHtmlService);
    expect(service.getAllSupportedTlds().length).toBe(1);
  });
});

In the 2nd test above we are creating a new FetchHtmlService and overriding the getRawHtml function behaviour by using Jasmine’s spyOn method.

That’s it! Now we don’t need a network connection to make actual calls while testing our code. We can develop and test our service in isolation independent from the dependency!

Resources

devdevops git

When you work on the same repository on multiple computers it’s easy to forget to push code and leave some changes local. To avoid that I decided to develop a tiny project that loops through all the folders and pulls remote changes then commits and pushes all the local changes.

The script itself it very simple. It first pulls the latest and pushes the local changes:

Function Sync-Git {
    git fetch --all
    git pull
    git add .
    git commit -m 'git-sync'
    git push
}

$rootFolder = '/rootFolder'

Write-Host "Changing directory to root folder: $rootFolder"
Set-Location $rootFolder
Get-Location | Write-Host

Get-ChildItem -Directory | ForEach-Object { 
    Set-Location -Path $_ 
    Write-Host $_
    Sync-Git
    Set-Location $rootFolder
}

Write-Host "Done"

Dockerfile is based Microsoft’s Powershell image and it only needs to install Git on it:

FROM mcr.microsoft.com/powershell

RUN apt-get update && apt-get install -y git
RUN git config --global user.email "you@example.com"

RUN echo "    IdentityFile ~/.ssh/id_rsa" >> /etc/ssh/ssh_config
RUN echo "    StrictHostKeyChecking no" >> /etc/ssh/ssh_config

RUN mkdir /home/git-sync
WORKDIR /home/git-sync

COPY ./git-sync.ps1 .

ENTRYPOINT ["pwsh", "git-sync.ps1"]

Build it at the root folder:

docker build . -t {image name}

And run it as:

docker run --rm -d -v /host/path/to/root/git/folder:/rootFolder -v ~/.ssh:/root/.ssh:ro {image name}

Or pull my image from Docker Hub:

docker pull volkanpaksoy/git-sync

Issues

fatal: unable to auto-detect email address

GitHub didn’t allow me to push my changes without an email address so I had to add the command below to Dockerfile:

git config --global user.email "you@example.com"

It doesn’t look good maybe but I don’t care too much about email addresses in commits anyway so I’m fine with it.

“WARNING: UNPROTECTED PRIVATE KEY FILE!” error

This might also be an issue in which case the solution is simple (assuming the name of the private key file is id_rsa):

chmod 600 ~/.ssh/id_rsa 

Bad configuration option

Make sure to have the following options in the config file in your .ssh directory:

Host *
  StrictHostKeyChecking no
  AddKeysToAgent yes
  IgnoreUnknown UseKeychain
  UseKeychain yes
  IdentityFile ~/.ssh/id_rsa

Source Code

Resources

dev javascript, tdd, unit_testing

There are so many names of tools and frameworks are flying around in JavaScript if you’re a beginner it’s easy to get lost.

First things first: Identify what’s what

Basically we need the following key components to write and run unit tests:

  • Assertions: We run a method in subject under test and compare the result with some known expected value
  • Mocking: We need to mock the external dependencies (network services, database, file system etc) so that we can reliably test subject under test and the tests can run everywhere and without requiring any credentials or permissions.
  • Test runner: Our test is some block of code. Something needs to make sense of the unit tests we wrote, execute them and show the results (fail/pass)

Choosing the weapons: Mocha and Chai

In this example I’m going to use Mocha as my test runner and Chai as my assertion library.

They can simply be installed via NPM:

npm install --save-dev chai

To be able use Mocha in the Integrated Terminal inside Visual Studio we have to install Mocha as a global package:

npm install -g mocha

To put these to test I created this simple Maths service that calculates the factorial of an integer.

module.exports = class MathsService {
    factorial(num) {
        if (num < 0) 
            return -1;
        else if (num == 0) 
            return 1;
        else {
            return (num * this.factorial(num - 1));
        }
    }
};

And my unit test to test this method looks like this:

const MathsService = require('../maths-service.js');
const chai = require('chai');
const expect = chai.expect; 

describe('Factorial', function() {
    it('factorial should return correct value', function() {
        const mathsService = new MathsService();
    
        expect(mathsService.factorial(-1)).to.equal(-1);
        expect(mathsService.factorial(0)).to.equal(1);
        expect(mathsService.factorial(1)).to.equal(1);
        expect(mathsService.factorial(3)).to.equal(6);
        expect(mathsService.factorial(5)).to.equal(120);
    });
});

Chai supports 3 styles:

  • Should
  • Expect
  • Assert

They all server the same purpose so it’s just a matter of taste at the end of the day. For this demo I used expect style.

By default Mocha searches test folder. So you can run mocha without parameters if you put your tests under test folder or you can specify the folder. For example, in the screenshot below I moved the test to the root folder and entered “mocha .” and that worked fine as well.

Conclusion

In this post I want to show the very basics of unit testing in JavaScript just enough to see a passing test. In future posts I’ll build on this and explore other frameworks and other aspects of TDD.

Resources