Test all the things - A test pyramide for Node.js Mircoservices

2bdb75b7d57685ed476e5ac061d835cf?s=47 June
April 05, 2017

Test all the things - A test pyramide for Node.js Mircoservices

2bdb75b7d57685ed476e5ac061d835cf?s=128

June

April 05, 2017
Tweet

Transcript

  1. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Test all the things! A Testing Pyramid for Node.js Microservices FrankfurtJS Meetup 5th of April, 2017 FrankfurtJS Meetup Test all the Things!
  2. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Our system consists of many Node.js microservices FrankfurtJS Meetup Test all the Things!
  3. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Ideally all kinds of tests ensure a stable system FrankfurtJS Meetup Test all the Things!
  4. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Our toolset to make unit tests easy to write and read FrankfurtJS Meetup Test all the Things!
  5. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Our toolset to make unit tests easy to write and read const chai = require('chai') const chaiAsPromised = require('chai-as-promised') const nock = require('nock') const AuthConnector = require('../app/connectors/Auth') chai.use(chaiAsPromised) const assert = chai.assert describe('AuthConnector', function () { let authConnector beforeEach(function () { authConnector = new AuthConnector() }) describe('#getToken()', function () { it('returns the token', function () { nock('http://authservice:3000').post('/token').reply(200, {token: 'testToken'}) return assert.becomes(authConnector.getToken('someId'), 'testToken') }) }) }) return assert.isRejected(promise) return promise.should.eventually.have.property(...) return expect(promise).to.eventually.have.same.members(...) FrankfurtJS Meetup Test all the Things!
  6. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Write testable modules/classes in the first place // Handler.js class Handler { constructor (authConnector) { this.authConnector = authConnector } addToken (body) { this.authConnector.getToken('someId').then((token) => { body.token = token return body }) } } module.exports = Handler // app.js ... const authConnector = new AuthConnector() const handler = new Handler(authConnector) ... FrankfurtJS Meetup Test all the Things!
  7. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Write testable modules/classes in the first place // Handler.test.js const chai = require('chai') const chaiAsPromised = require('chai-as-promised') const sinon = require('sinon') const Handler = require('../app/Handler') chai.use(chaiAsPromised) const assert = chai.assert sinon.assert.expose(chai.assert, { prefix: '' }) describe('Handler', function () { let authConnector let handler beforeEach(function () { authConnector.getToken = sinon.stub().returns(Promise.resolve('testToken')) handler = new Handler(authConnector) }) describe('#addToken()', function () { it('adds the token to the input object', function () { return handler.addToken({foo: 'bar'}) .then(() => { assert.calledWith(authConnector.getToken, {foo: 'bar'}) }) }) }) }) FrankfurtJS Meetup Test all the Things!
  8. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Avoid testing the test instead of the real code // Handler.js ... forwardRequest (body) { return this._makeRequest(params) } _makeRequest (body) { const params = { url: ..., headers: {...}, body } return requestPromise(params) } ... // Handler.test.js ... describe('forwardRequest', function () { it('makes the request', function () { // DON'T DO THIS handler._makeRequest = function () { return Promise.resolve('result') } const promise = handler.forwardRequest({foo: 'bar'}) return assert.becomes(promise, 'result') }) }) ... FrankfurtJS Meetup Test all the Things!
  9. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests We write integration tests in a language agnostic way #readAppInfo.feature Feature: App information can be read Background: Given I set property request body to testdata app-info And I set the request header accept-version with value ~1 And I send a POST request to /devices/d123/apps/a123 Scenario: App information can be retrieved Given I set the request header accept-version with value ~1 When I send a GET request to /devices/d123/apps/a123 Then the response status code is 200 And I check property response body equals testdata app-info Scenario: Wrong url is used Given I set the request header accept-version with value ~1 And I send a GET request to /device/d123/app/a123 Then the response status code is 404 Scenario: Version not supported Given I set the request header accept-version with value ~999 And I send a GET request to /devices/d123/apps/a123 Then the response status code is 400 FrankfurtJS Meetup Test all the Things!
  10. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Custom test steps can be added if needed // steps.js const path = require('path') const config = require('../../../config') module.exports = function customSteps () { require('minosse').call(this) this.Before(function loadTestConfig (scenario, done) { // minosse checks for `testConfig` for framework configuration this.testConfig = { testDataRoot: path.join(__dirname, '../data') } done() }) this.Given(/^I set the test application to (\S+)$/, function changeApp (app, done) { this._log.info(`Step: I set the test application to ${app}`) this.testConfig.defaultHost = config.integrationTest.host this.testConfig.defaultPort = config.integrationTest[app].port done() }) } FrankfurtJS Meetup Test all the Things!
  11. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests We use docker-compose to boot up all necessary components for the test version: '2' services: service-a: build: . environment: - MYSQL_HOST=mysql command: npm run watch links: - mysql - service-b ports: - 3000:3000 restart: always volumes: - ./app:/src/app - ./app.js:/src/app.js service-b: image: somehost/service-b:latest ports: - 4000:4000 environment: - SERVICE_A_URL=http://service-a:3000 - CASSANDRA_HOSTS=cassandra.local:9042 links: - cassandra:cassandra.local cassandra: image: spotify/cassandra mysql: image: mysql environment: - MYSQL_ROOT_PASSWORD=somepassword FrankfurtJS Meetup Test all the Things!
  12. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests For end-2-end tests we use Minosse to hit the entrypoint of the system like the apps would do FrankfurtJS Meetup Test all the Things!
  13. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests The docker-compose file for the E2E tests mimic the real system as close as possible some-nginx: build: context: ./somepath dockerfile: ./Dockerfile restart: always ports: - 80:80 consul: image: consul container_name: consul command: agent -server -client=0.0.0.0 -bind=0.0.0.0 -ui -bootstrap ports: - 8500:8500 registrator: image: gliderlabs/registrator container_name: registrator volumes: - /var/run/docker.sock:/tmp/docker.sock command: -internal consul://consul:8500 links: - consul syslog: build: somehost/syslogdummy container_name: syslog FrankfurtJS Meetup Test all the Things!
  14. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Running the whole system locally with docker-compose is a blessing Advantages Connect with mobile devices to the local PC Possible to plug in code for a specific shop to test it Edit different services and see the result immediately Catch configuration errors early on locally FrankfurtJS Meetup Test all the Things!
  15. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests We run extensive smoke tests in all stages Smoke test deploys a whole new test shop container into the system Then runs DB-neutral test cases and removes the shop Swarm, consul etc. is utilized during deployment and problems can be catched Allows to catch AWS problems FrankfurtJS Meetup Test all the Things!
  16. The system and the pyramid Unit tests Integration & E2E

    tests Smoke tests Links https://mochajs.org/ http://chaijs.com/ https://github.com/domenic/chai-as-promised http://sinonjs.org/ https://github.com/icemobilelab/minosse FrankfurtJS Meetup Test all the Things!