Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a suggested way to write unit tests with Mocha/Chai/Sinon? #171

Closed
Tobias4872 opened this issue Jan 3, 2017 · 10 comments
Closed

Comments

@Tobias4872
Copy link
Contributor

Hi,

I am currently looking for an ORM to support a REST API written with Koa. I wonder whether there are some examples of how unit testing will work with typeorm let's say with Mocha/Chai/Sinon? Are there probably any mock classes for connections, repositories, etc.? I have scanned the repo and found files for internal testing purposes, but there are not distributed.

Any comment would be appreciated,

Tobias.

@pleerock
Copy link
Member

pleerock commented Jan 4, 2017

Hi, no there are no extra tools for that in this orm. But I would like to work in this direction, so if you have suggestions what extra tools should be added to typeorm to improve user experience of writing unit tests - we can discuss and add them.

@Tobias4872
Copy link
Contributor Author

I will give it a try and come back with suggestions. Sorry, did close to early.

@Tobias4872 Tobias4872 reopened this Jan 4, 2017
@Tobias4872
Copy link
Contributor Author

Sorry for the delay. I have done a bit of work on the API and I have also done some unit tests. As the requests to the db have been quite simple so far, I have been putting spys on a mock repository and am asserting on the parameters passed to find, findOneById, persist, etc. This works, but I now have to build more complex queries using the query builder. And I am not quite sure where to hook in now to assert what happens.

Let's assume that there would be a specific driver (or driver type) specifically designed to be used during unit testing, and let's further assume that there is a place in the code where finally all queries need to be passed through, I'd vote for a possibility to spy on this place to assert on the query object or plain SQL passed through it. In case of a query object, it should be possible to test on fields selected, tables joined, where clauses set, etc. In case of SQL, the SQL could be using a specific or standard SQL dialect which would always return the same results.

Does this make sense?

@pleerock
Copy link
Member

I need real world examples from you, small as possible to reproduce your issues and try to implement what you are suggesting. Can you collaborate in this direction by providing some repo with such sample?

@Tobias4872
Copy link
Contributor Author

Sorry for the delay. I have been crafting an example at https://github.com/Tobias4872/typeorm-unit-testing. Please see the README file for details.

@craig552uk
Copy link

I've been working on an Express/TypeORM boilerplate recently which uses Mocha and Supertest.
https://github.com/craig552uk/express-typeorm-boilerplate/blob/master/src/route/productRouter.test.ts#L56-L75

@valVk
Copy link

valVk commented Mar 30, 2017

Thanks, very promising.
In my turn I'm starting to use GulpClass there is simple starter which was inspired mostly of GulpClass of Typeorm

///<reference path="./node_modules/@types/node/index.d.ts"/>
///<reference path="./node_modules/@types/chai/index.d.ts"/>
///<reference path="./node_modules/@types/mocha/index.d.ts"/>

import {
  Gulpclass
  , Task
  , SequenceTask
  // , MergedTask
} from 'gulpclass';

const gulp = require('gulp');
const del = require('del');
const shell = require('gulp-exec');
// const replace = require('gulp-replace');
// const rename = require('gulp-rename');
// const file = require('gulp-file');
// const uglify = require('gulp-uglify');
const mocha = require('gulp-mocha');
const chai = require('chai');
const tslint = require('gulp-tslint');
const stylish = require('tslint-stylish');
// const sourcemaps = require('gulp-sourcemaps');
const istanbul = require('gulp-istanbul');
const remapIstanbul = require('remap-istanbul/lib/gulpRemapIstanbul');
// const ts = require('gulp-typescript');
const args = require('yargs').argv;

@Gulpclass()
export class Gulpfile
{
  /**
   * Cleans dist folder.
   */
  @Task()
  clean(cb: Function) {
    return del(['./dist/**', './coverage/**', './coverage-remap/**'], cb);
  }

  @Task()
  compile() {
    return gulp.src('package.json', { read: false })
      .pipe(shell(['tsc']))
      .pipe(shell.reporter());
  }

  /**
   * Runs before test coverage, required step to perform a test coverage.
   */
  @Task()
  coveragePre() {
    return gulp.src(['./dist/server/**/*.js'])
      .pipe(istanbul())
      .pipe(istanbul.hookRequire());
  }

    /**
   * Runs post coverage operations.
   */
  @Task('coveragePost', ['coveragePre'])
  coveragePost() {
    chai.should();
    chai.use(require('sinon-chai'));
    chai.use(require('chai-as-promised'));

    return gulp.src(['./dist/server/test/*Tests.js'])
      .pipe(mocha({
          bail: true,
          grep: !!args.grep ? new RegExp(args.grep) : undefined,
          timeout: 15000
      }))
      .pipe(istanbul.writeReports());
  }

  /**
  * Shows coverage for TS files instead of JS
  */
  @Task()
  coverageRemap() {
    return gulp.src('./coverage/coverage-final.json')
      .pipe(remapIstanbul({
        reports: {
            'html': './coverage-remap',
            'text-summary': null,
            'lcovonly': './coverage/lcov.info'
        }
      }))
      .pipe(gulp.dest('./coverage'));
  }

  /**
   * Compiles the code and runs tests + makes coverage report.
   */
  @SequenceTask()
  tests() {
    return ['clean', 'compile', 'coveragePost', 'coverageRemap'
    ];
  }
}

selection_20170330_875ba5f

@KyleGalvin
Copy link

@pleerock I've been working on mocking repositories so I can make tests that work on Travis-CI without a DB. Creating a simple IRepository interface has allowed me a lot of flexibility. I can use TypeMoq to automatically create a mock that conforms to the interface, while also allowing me the flexibility of overriding it with test behavior.

Perhaps you would consider my pull request:
#1183
And an example of use:
https://github.com/KyleGalvin/backendBoilerplate/blob/master/test/authProvider.ts

@mscharley
Copy link

mscharley commented Nov 29, 2017

I've had some really good luck with the testdouble package.

For instance, to integrate with dataloader:

// TypeormDataLoader.ts
import * as DataLoader from "dataloader";
import { Repository } from "typeorm";

export const typeOrmDataloader = <T, K extends keyof T>(repository: Repository<T>, keyProperty: K) =>
  new DataLoader<T[K], T | undefined>(async (keys) => {
    const es = await repository.createQueryBuilder("e")
      .where(`e.${keyProperty} IN (:keys)`, { keys })
      .getMany();

    return keys.map((k) => es.find((e) => e[keyProperty] === k));
  });
// TypeormDataLoader.spec.ts
import { expect } from "chai";
import { slow, suite, test, timeout } from "mocha-typescript";
import * as td from "testdouble";
import { Repository, SelectQueryBuilder } from "typeorm";
import { Portal } from "./Portal";
import { typeOrmDataloader } from "./TypeormDataLoader";

@suite(timeout(300), slow(50))
export class TypeormDataLoaderSpec {
  public after() {
    td.reset();
  }

  @test public async testSuccess() {
    const repo = td.object<Repository<Portal>>("Repository");
    const query = td.object<SelectQueryBuilder<Portal>>("SelectQueryBuilder");

    td.when(repo.createQueryBuilder("e")).thenReturn(query);
    td.when(query.where("e.uuid IN (:keys)", { keys: ["abcdef"] })).thenReturn(query);
    td.when(query.getMany()).thenReturn([{ uuid: "abcdef" }]);

    const dl = typeOrmDataloader(repo, "uuid");
    await expect(dl.load("abcdef")).to.eventually.deep.equal({ uuid: "abcdef" });
  }
}

@pleerock
Copy link
Member

Let's keep discussion in a single thread. Closing in favour of #1267.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants