diff --git a/package.json b/package.json index 1e9083d1cd..9410aa9778 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@types/lodash": "^4.14.34", "@types/log4js": "0.0.32", "@types/mkdirp": "^0.3.28", - "@types/mocha": "^2.2.34", + "@types/mocha": "^2.2.42", "@types/mz": "0.0.31", "@types/progress": "^1.1.28", "@types/rimraf": "0.0.28", diff --git a/packages/stryker-mocha-runner/src/MochaTestRunner.ts b/packages/stryker-mocha-runner/src/MochaTestRunner.ts index 8f4e55bf92..7af7148ca3 100644 --- a/packages/stryker-mocha-runner/src/MochaTestRunner.ts +++ b/packages/stryker-mocha-runner/src/MochaTestRunner.ts @@ -2,10 +2,7 @@ import * as log4js from 'log4js'; import { EventEmitter } from 'events'; import { TestRunner, RunResult, RunStatus, RunnerOptions } from 'stryker-api/test_runner'; import { InputFile } from 'stryker-api/core'; - - -// import * as Mocha from 'mocha'; -const Mocha = require('mocha'); +import * as Mocha from 'mocha'; import StrykerMochaReporter from './StrykerMochaReporter'; const log = log4js.getLogger('MochaTestRunner'); @@ -25,12 +22,23 @@ export default class MochaTestRunner extends EventEmitter implements TestRunner return new Promise((resolve, fail) => { try { this.purgeFiles(); - let mocha = new Mocha({ reporter: StrykerMochaReporter, bail: true }); + let mocha = new Mocha({ reporter: StrykerMochaReporter as any, bail: true }); this.files.filter(file => file.included).forEach(f => mocha.addFile(f.path)); try { - let runner: any = mocha.run((failures: number) => { - let result: RunResult = runner.runResult; - resolve(result); + mocha.run((failures: number) => { + const reporter = StrykerMochaReporter.CurrentInstance; + if (reporter) { + let result: RunResult = reporter.runResult; + resolve(result); + } else { + const errorMsg = 'The StrykerMochaReporter was not instantiated properly. Could not retrieve the RunResult.'; + log.error(errorMsg); + resolve({ + tests: [], + errorMessages: [errorMsg], + status: RunStatus.Error + }); + } }); } catch (error) { resolve({ diff --git a/packages/stryker-mocha-runner/src/StrykerMochaReporter.ts b/packages/stryker-mocha-runner/src/StrykerMochaReporter.ts index a01f8a3434..c7fa9e54cf 100644 --- a/packages/stryker-mocha-runner/src/StrykerMochaReporter.ts +++ b/packages/stryker-mocha-runner/src/StrykerMochaReporter.ts @@ -6,12 +6,15 @@ const log = log4js.getLogger('StrykerMochaReporter'); export default class StrykerMochaReporter { - private runResult: RunResult; + public runResult: RunResult; private timer = new Timer(); private passedCount = 0; + static CurrentInstance: StrykerMochaReporter | undefined; + constructor(private runner: NodeJS.EventEmitter) { this.registerEvents(); + StrykerMochaReporter.CurrentInstance = this; } private registerEvents() { @@ -53,7 +56,6 @@ export default class StrykerMochaReporter { this.runner.on('end', () => { this.runResult.status = RunStatus.Complete; - (this.runner as any).runResult = this.runResult; log.debug(`Mocha test run completed: ${this.passedCount}/${this.runResult.tests.length} passed`); }); } diff --git a/packages/stryker-mocha-runner/test/integration/MochaTestRunnerSpec.ts b/packages/stryker-mocha-runner/test/integration/MochaTestRunnerSpec.ts index 71ef997b32..6a8eba935e 100644 --- a/packages/stryker-mocha-runner/test/integration/MochaTestRunnerSpec.ts +++ b/packages/stryker-mocha-runner/test/integration/MochaTestRunnerSpec.ts @@ -87,5 +87,28 @@ describe('MochaTestRunner', function () { })); }); - let file = (filePath: string, mutated: boolean = true, included: boolean = true) => ({ path: path.resolve(filePath), mutated, included }); + describe('when no tests are executed', () => { + + beforeEach(() => { + const testRunnerOptions = { + files: [ + file('./testResources/sampleProject/src/MyMath.js')], + strykerOptions: {}, + port: 1234 + }; + sut = new MochaTestRunner(testRunnerOptions); + }); + + it('should report no completed tests', () => + expect(sut.run()).to.eventually.satisfy((runResult: RunResult) => { + expect(countSucceeded(runResult)).to.be.eq(0, 'Succeeded tests did not match'); + expect(countFailed(runResult)).to.be.eq(0, 'Failed tests did not match'); + runResult.tests.forEach(t => expect(t.timeSpentMs).to.be.greaterThan(-1).and.to.be.lessThan(1000)); + expect(runResult.status).to.be.eq(RunStatus.Complete, 'Test result did not match'); + expect(runResult.coverage).to.not.be.ok; + return true; + })); + }); + + let file = (name: string, mutated: boolean = true, included: boolean = true) => ({ path: path.resolve(name), mutated, included }); }); \ No newline at end of file diff --git a/packages/stryker/src/Stryker.ts b/packages/stryker/src/Stryker.ts index db086c620d..70a05f1cd8 100644 --- a/packages/stryker/src/Stryker.ts +++ b/packages/stryker/src/Stryker.ts @@ -71,7 +71,11 @@ export default class Stryker { const inputFiles = await new InputFileResolver(this.config.mutate, this.config.files).resolve(); const { runResult, sandboxCoordinator } = await this.initialTestRun(inputFiles); - if (runResult && inputFiles && sandboxCoordinator) { + + if (runResult.tests.length === 0) { + log.warn('No tests were executed. Stryker will exit prematurely. Please check your configuration.'); + return []; + } else if (runResult && inputFiles && sandboxCoordinator) { const mutantResults = await this.generateAndRunMutations(inputFiles, runResult, sandboxCoordinator); const score = ScoreResultCalculator.calculate(mutantResults); this.reporter.onScoreCalculated(score); diff --git a/packages/stryker/test/unit/StrykerSpec.ts b/packages/stryker/test/unit/StrykerSpec.ts index 987580da4f..0b1456f593 100644 --- a/packages/stryker/test/unit/StrykerSpec.ts +++ b/packages/stryker/test/unit/StrykerSpec.ts @@ -35,7 +35,7 @@ describe('Stryker', function () { let testFrameworkOrchestratorMock: Mock; let configValidatorMock: Mock; let sandboxCoordinatorMock: Mock; - let configReaderMock: Mock; + let configReaderMock: Mock; let pluginLoaderMock: Mock; let inputFiles: InputFile[]; let determineExitCodeStub: sinon.SinonStub; @@ -43,7 +43,7 @@ describe('Stryker', function () { let config: any; let mutants: any[]; let reporter: Reporter; - + beforeEach(() => { sandbox = sinon.sandbox.create(); config = {}; @@ -288,6 +288,25 @@ describe('Stryker', function () { }); }); }); + + describe('with no tests executed', () => { + beforeEach(() => { + resolveInitialTestRun({ + status: RunStatus.Complete, + tests: [] + }); + }); + + it('should log to have quit early', async () => { + await sut.runMutationTest(); + expect(log.warn).to.have.been.calledWith('No tests were executed. Stryker will exit prematurely. Please check your configuration.'); + }); + + it('should not have tested mutations', async () => { + await sut.runMutationTest(); + expect(sandboxCoordinatorMock.runMutants).not.to.have.been.called; + }); + }); }); }); });