Search code examples
cssangularwebpackkarma-runnertestbed

Angular unit testing - ignore stylesheets


I'm trying to speed up the unit tests for a fairly large non-cli Angular application and had a thought: what if I skipped the style sheets? The slowest step in running the tests (by a wide margin) is Webpack compiling the thousands of scss style sheets contained in the app.

I've changed the webpack settings to load empty modules for these files:

 { test: /\.css$/, use: 'null-loader' },
 { test: /\.scss$/, use: 'null-loader' },

But of course the Angular testbed's metadata-resolver now complains about the modules being empty..

 Error: Expected 'styles' to be an array of strings.
      at assertArrayOfStrings (webpack:///node_modules/@angular/compiler/esm5/compiler.js:2522 <- config/spec-bundle.js:109446:19)
      at CompileMetadataResolver.getNonNormalizedDirectiveMetadata (webpack:///node_modules/@angular/compiler/esm5/compiler.js:14965 <- config/spec-bundle.js:121889:13)

I think what I need to do here is to either load every style sheet as an empty string, or set the Testbed up in such a way that it ignores references to .scss files in the component metadata.

Is there a way to accomplish one of these solutions, or is there perhaps a smarter way of going about this?


Solution

  • I solved it!

    By creating a custom loader that loads all .scss-files as empty strings I can drastically reduce the time it takes to compile my unit tests.

    ignore-sass-loader.js:

    const sass = require("node-sass");
    const async = require("neo-async"); const threadPoolSize = process.env.UV_THREADPOOL_SIZE || 4;
    const asyncSassJobQueue = async.queue(sass.render, threadPoolSize - 1);
    
    module.exports = function ignoreSassLoader(content) { 
        const callback = this.async(); 
    
        asyncSassJobQueue.push({}, () => { 
            callback(null, ' '.toString(), null); 
        }); 
    };
    

    This is then resolved by adding an alias to the webpack configuration:

    module.exports = function () {
        return {
                   resolveLoader: {
                       alias: {
                           'ignore-sass-loader': resolve(__dirname, '../config/loaders/ignore-sass-loader')
                       }
                   },
    

    And then finally I use my loader together with the raw-loader:

    {
         test: /\.scss$/,
         use: ['raw-loader', 'ignore-sass-loader'],
    },