Windows 8, Mac OSX El Capitan
angular-cli: 1.0.0-beta.19-3
node: 6.3.0
os: win32 x64
This is a feature request. The ability to run a single .spec.ts
file would be excellent, when you guys get the chance. I recently switched from using Angular1 in Visual Studio where one could use the Test Explorer or the Chutzpah extension to run a test on a single component. Working on a large enterprise application, it's a huge pain to run the entire unit test suite (which takes a few minutes) every time I refactor a single component (and thus only need to run that component's tests).
You should be able to do fdescribe or fit instead of describe or it with Jasmine which forces a test.
There is also xdescribe and xit which skips tests
Dup of #3436?
You can temporarily change the following line in the test.ts
file, to restrict the test-suite to one particular test-specification:
...
// Then we find all the tests.
let context = require.context('./', true, /\.spec\.ts/);
...
For example, if you want to run only the test-file hello-world.spec.ts, just change the file, like this:
...
// Then we find all the tests.
let context = require.context('./', true, /hello-world\.spec\.ts/);
...
Before push, you should run all the test-cases, so you can remove the changes of test.ts.
@zsfarkas Yeah, I'm aware of that option. I'd prefer something that doesn't require editing any of the source files, though. In our webpack-based build, we did this by passing a command-line param w/ the folder path into the ContextReplacementPlugin, but we don't have access to the webpack config in the CLI, and I don't know of a way to get a command line arg into test.ts
.
@mattdistefano Ok, that would be a dream ;-)
While the solution of changing code to make a single test run technically works, it's really not feasible when you have an application with hundreds of components. TDD only works well if you can have fast, frictionless cycle of test/code/test/code/test/code. Running the entire app's suites, x-ing out suites, or changing karma config, every time a single component is changed, makes that cycle very slow.
Ok so we have fdescribe
and friends, plus chaging test.ts
. Those are two options that exist right now.
@mattdistefano mentioned the grep
option in https://github.com/angular/angular-cli/issues/3436, but I can't find any documentation. Can you refer me to it?
I'm not aware of any facility to only run tests related to the file that changed however. I don't think karma supports such a thing. It's not as simple as running name.spec.ts
when name.ts
changes, since tests might use use classes from other files.
@filipesilva Sorry about the late reply - I think it's actually only available in the mocha plugin, not karma itself.
I'm new in angular-cli, so my solution can be not perfect, but it's work (tested in angular v.4.2.4 application created with angular CLI v.1.1.3 on os X and ubuntu).
npm install --save-dev minimist
package.json
file scripts
section test
field to"test": "ng test --watch=false --code-coverage --tags $tags"
NOTE: --watch=false --code-coverage
need to me for code coverage. If you don't use this feature, you can simplify it to
"test": "ng test --tags $tags"
karma.conf.js
. At top of file addconst argv = require('minimist')(process.argv.slice(2)),
tags = (argv.tags !== true) && argv.tags;
and in config
object in client
field (that is also object) add field args
client: {
args: [tags],
clearContext: false // leave Jasmine Spec Runner output visible in browser
}
src/test.ts
. Afterdeclare const __karma__: any;
declare const require: any;
add
const tags = __karma__.config.args[0];
and change
// then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// and load the modules.
context.keys().map(context);
// finally, start Karma to run the tests.
__karma__.start();
to
// then we find all the tests.
const filterRegExp = (tags) ? new RegExp(tags, 'g') : /\.spec\.ts$/,
context = require.context('./', true, /\.spec\.ts$/),
specFiles = context.keys().filter(path => filterRegExp.test(path));
// and load the modules.
specFiles.map(context);
// finally, start Karma to run the tests.
__karma__.start();
That's all. Now to launch all unit tests
npm test
to launch just tests that located in footer.component.spec.ts
file
npm test footer.component
Changing a test definition's it(..
to fit(...
will cause only those tests marked as fit
to run. If you want to run a specific file's tests, do a find and replace in that file to change it(
to fit(
. This is working great for me in my test driven development process.
@EugeneSnihovsky that works, however, it seems (at least in my case) webpack is still building the entire project. As such, there's no reason to use this over using fdescribe
or fit
since it does not offer any performance advantage. In fact, sans watching the build, it's slower since it needs to rebuild each time. I think the end goal should be to not build the entire project in order to run a single test.
a feature passing the spec name or a regex pattern like jest has it, would indeed by nice. i know about fit and xit, however its not optimal. you have to replace all its in the spec and even more important you might forget to unfit before committing.
How about allowing to choose the test runner/framework all together?
For example, one could simply use jest or any other test runner that comes on to disrupt the status quo... Or create own...
Either way, removing the locking to anything, has proved useful before
Wait not implementing a test:tdd mode. Which watch for changes in particular file. And not rebundle all app, but only this part, HMR style. We were doint this with grunt / tsc, injecting into karma only the needed spec file associated with either the modified spec file itself or its "pair" file (exemple: changes on app.component.ts
and app.component.spect.ts
will trigger the test and this one only since the other ones didn't change).
Thumbs up for not needing to edit all my test sources nor hack around if I want to run just one test spec. This is something I've been able to do with Maven+JUnit since the beginning of time
We had to do the same for our app as we need to run single spec file and also include the coverage for single source file that is tested. For this we had to override test file in cli package and also use custom tasks to change test.ts and spec.json. If anyone interested I can share the solution...
@mlakmal I am and for sure many others could be interested in your solution
@blackholegalaxy on our app we have a postinstall script that will override node_modules with our custom files (we use this to override some angular-cli and htmllint package files for our requirements)
assumptions: spec file has the same name as source file. Ex: httpClient2.ts, httpClient2.spec.ts
we support running unit test for single spec file or all spec files inside specific folder. we use command line switches to specify these options.
Ex:
gulp test --file=httpClient2
gulpt test --folder=login
below is the change we added to node_modules/@angular/cli/models/webpack-configs/test.js
as par of out gulp command we set node global variables process.env.npm_config_file
process.env.npm_config_folder
that can be used from angular cli since it doesn't read command line arguments directly.
if (buildOptions.codeCoverage && config_1.CliConfig.fromProject()) {
//regex used to identify spec file to be executed
var tReg = new RegExp('\\.spec\\.ts$', 'i');
//regex used to identify source file for code coverage
var cReg = new RegExp('\\/(?!.*(test|mocks|Module|interfaces|values|enums|public_api)).*\\.(ts|js)$', 'i');
if (process.env.npm_config_file) {
tReg = new RegExp(process.env.npm_config_file + '\\.spec\\.ts$', 'i');
cReg = new RegExp(process.env.npm_config_file + '\\.ts$', 'i');
}
else if (process.env.npm_config_folder) {
tReg = new RegExp('\\/' + process.env.npm_config_folder+ '\\/(.*)\\.spec\\.ts$', 'i');
cReg = new RegExp('\\/' + process.env.npm_config_folder+ '\\/(?!.*(test|mocks|Module|interfaces|values|enums|public_api)).*\\.ts$', 'i');
}
const codeCoverageExclude = config_1.CliConfig.fromProject().get('test.codeCoverage.exclude');
let exclude = [
tReg,
/node_modules/
];
if (codeCoverageExclude) {
codeCoverageExclude.forEach((excludeGlob) => {
const excludeFiles = glob
.sync(path.join(projectRoot, excludeGlob), { nodir: true })
.map(file => path.normalize(file));
exclude.push(...excludeFiles);
});
}
extraRules.push({
test: **cReg**, loader: 'istanbul-instrumenter-loader',
options: { esModules: true },
enforce: 'post',
exclude
});
}
main spec tsconfig file - we use another json file (test.cli.custom.ts) that gets created dynamically with unit test command
{
"extends": "../tsconfig.cli.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"node",
"jquery",
"custom",
"systemjs"
],
"paths": {
}
},
"files": [
"test.cli.custom.ts"
],
"include": []
}
below is the dynamic created tsconfig.cli.custom.json file this changes depending on command line switch for test command.
include section of below file will be changing according to unit test single file/folder or all files.
this is the file specified in .angular-cli.json for spec tsconfig
{"extends": "./tsconfig.spec.cli.json", "include": ["**/*.spec.ts","**/*.d.ts"]}
below is our main test.ts file
import { CustomDatePipe, CustomCurrencyPipe } from '@anthem/shared';
import { DatePipe, CurrencyPipe } from '@angular/common';
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare const __karma__: any;
declare const require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = () => {
//nop
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
we also create custom test.cli.custom.ts file to specify which files to be loaded depending on single file/folder or all files in unit test command. require.context
section below will use different regex depending on our command.
we use this ts file as the main entry point for test command in .angular-cli.json
import './test.cli';
declare
const __karma__: any;
declare
const require: any;
const context = require.context('./', true, /\.spec\.ts$/i);
context.keys().map(context);
__karma__.start();
this allows us to reduce the time it takes on compilation when developers only want to work on single spec file or all files under specific folder. and code coverage works properly to show only the coverage for testing file instead of all files.
Any updates on this issue?
Asked this question in a class I'm teaching today and was surprised to see it's still not available, given the grep feature of Karma. This is a productivity issue. Jest for example runs in a mode where only the most recent changes get fed through the testing engine. For a toy project what we have is fine, but for a bigger project with lots of tests, asking developers to add fit or fdescribe or to edit the karma.conf.js file is a bit much. Then you'd need to make sure to fail build checkin if anyone leaves fit or fdescribe in the test source. Seems like a lot of issues instead of adding a single additional flag to Karma from ng test.
A variation on the popular answer from @EugeneSnihovsky but this approach does not have the minimist dependency, better error handling if no spec file can be found and allows to run one or two or more tests:
(1) DO NOT ADD MINIMIST DEPENDECY
(2) DO NOT MODIFY package.json
(3)
Modify karma.conf.js. At top of file add:
const tags = process.argv.filter(arg => {
return (arg.endsWith('.component') || arg.endsWith('.service'))
})
and in config object in client field (that is also object) add field args:
client: {
args: [tags],
clearContext: false // leave Jasmine Spec Runner output visible in browser
}
(4)
Change code in src/test.ts to:
declare var __karma__: any
declare var require: any
const tags = __karma__.config.args[0]
// Prevent Karma from running prematurely.
__karma__.loaded = function () {}
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
)
let filterRegex
if (tags.length) {
const strTags = tags.join('|')
filterRegex = new RegExp(`(${strTags})\\.spec\\.ts$`) // SPECIFIC TEST SUITES AS PASSED TO ... npm test filter-list.component filter-list.service
} else {
filterRegex = /\.spec\.ts$/ // ALL TEST SUITES
}
// filterRegex = /(filter-list.component|filter-list.service)\.spec\.ts$/ // OR YOU CAN UNCOMMENT AND EDIT THIS LINE
const context = require.context('./', true, /\.spec\.ts$/)
const specFiles = context.keys().filter(path => filterRegex.test(path))
if (specFiles.length) {
specFiles.map(context)
__karma__.start()
} else {
console.warn(`Could not find any test suites for ${tags.join(', ')}`)
}
(5) Run your tests with ...
ng test (to test all)
OR
ng test your-cool.component your-cool.service
OR
npm test (to test all)
OR
npm test your-cool.component your-cool.service
@danday74 do you think it's possible to make a CLI plugin for that?
it -> fit worked for my use case where I am "focusing" on specific tests. When I'm done I can fit -> it, and commit. (curious the consequences of fit in build pipeline... we'd want to force all fit and it to be executed).
fdescribe
and fit
are kind of ok solutions when you don't have many tests, but it slows down work later on. The perfect solution would be to be able to run ng test --spec=common/services/**
which would build only the needed files.
I'll poke around and try to port the solution I have at work that involves temporary/generated test.ts that lists needed files instead of relying on require.context
Final solution for our team was to just use Jest.
Final solution for our team was to just use Jest.
Can you give an example, or link to one? I'd be interested.
@BirgitPohl we used the library https://github.com/thymikee/jest-preset-angular. They mention in their readme a tutorial example. It worked with minor adjustments (because we use aliases in tsconfig we had to put back in jest.config.js
).
@BirgitPohl
What @blackholegalaxy said. Just follow the readme and everything should work out of the box.
Cool. That's Jest-specific, isn't it? I'm using Jasmine in my tests. I wonder if it has something like this too.
Im also looking for this. I would like a VS CODE plugin or something to test a single test each time im updating that test... I don't want to go into test.ts and change that regex :(
I updated Angular in my app from version 5.0.5 to version 7.2.4.
With version 5.0.5 I used the method that suggested @EugeneSnihovsky and everything worked well. After the upgrade tests do not run.
I use command "test": "ng test --watch=false --code-coverage --tags $tags"
. And in terminal i see Unknown option: '--tags'
:(
Unfortunately for now I can't find any workaround to pass custom flag to angular cli. As quick and dirty workaround I modified this solution
NOTE: only edits are shown below, not complete solution
const argv = require('minimist')(process.argv.slice(2)),
tags = (argv.tags !== true) && argv.tags;
to
const tags = (process.env.tags !== 'true') && process.env.tags;
"scripts": {
"test": "ng test --watch=false --code-coverage"
}
tags=footer.component npm test
If you can't change test launch syntax (for example like me because of automatic scripts on gitlab CI) - you can implement more dirty workaround
const gulp = require('gulp'),
shell = require('gulp-shell'));
gulp.task('ng-test', (callback) => runUnitTests(callback, false));
gulp.task('ng-test:watch', (callback) => runUnitTests(callback, true));
function runUnitTests(callback, watchMode) {
let argv = require('minimist')(process.argv.slice(2));
shell.task([
`tags=${argv.tags} ng test --watch=${watchMode} --code-coverage`
])(callback);
}
"scripts": {
"gulp": "gulp",
"test": "gulp ng-test --tags $tags",
"test:watch": "gulp ng-test:watch --tags $tags"
}
npm test footer.component
@EugeneSnihovsky, I did the changes as you mentioned, but it tells me the "test.component" could not be found in workspace.
ng test test.component
Do we need to give the whole path or it takes automatically?
@trupti28 probably you should use
npm test test.component
instead of
ng test test.component
When I try this I simply get the error output:
Project 'myTest.component' could not be found in workspace
so I add the --project flag (I have multiple projects in the workspace)
Then I get Unknown option: 'myTest.component'
With the command: ng test --project=my-project myTest.component
Solution for angular 8
Tested in OS X with "@angular-devkit/build-angular": "~0.802.2"
Modify your package scripts to
"scripts": {
"test": "function test() { cmd='ng test --watch=false --code-coverage'; if [ -n \"$1\" ]; then cmd=\"$cmd --include $1\"; fi; $cmd; }; test"
}
Usage has changed a bit
npm test **/sign-in.component.spec.ts
P.S: thanks @RomanKisilenko for help.
Hi,
"test": "function test() { cmd='ng test --watch=false --code-coverage'; if [ -n \"$1\" ]; then cmd=\"$cmd --include $1\"; fi; $cmd; }; test",
Not working for me with "@angular-devkit/build-angular": "^0.801.3",
! This is mandatory?
@pacocom I am fairly sure that command referenced above only works on linux/mac and via bash command on windows. From the looks in the screenshot you are running it via standard cmd
I don't know!
I upadate to "@angular-devkit/build-angular": "^0.803.3",
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
I'm new in angular-cli, so my solution can be not perfect, but it's work (tested in angular v.4.2.4 application created with angular CLI v.1.1.3 on os X and ubuntu).
package.json
filescripts
sectiontest
field to"test": "ng test --watch=false --code-coverage --tags $tags"
NOTE:
--watch=false --code-coverage
need to me for code coverage. If you don't use this feature, you can simplify it to"test": "ng test --tags $tags"
karma.conf.js
. At top of file addand in
config
object inclient
field (that is also object) add fieldargs
src/test.ts
. Afteradd
and change
to
That's all. Now to launch all unit tests
to launch just tests that located in
footer.component.spec.ts
file