Mon application fonctionne comme un charme, mais je ne peux pas exécuter mes tests avec

$ yarn test
# node node_modules/karma/bin/karma start ./karma.conf.js --single-run

Version

    "angular-mocks": "~1.5.10",
    "karma-webpack": "^2.0.1",
    "webpack": "^1.14.0",
    "webpack-dev-server": "^1.16.2",

Erreur

PhantomJS 2.1.1 (Linux 0.0.0) leave API service create(): should create a leave FAILED
        Error: [$injector:unpr] Unknown provider: LeaveServiceProvider <- LeaveService
        http://errors.angularjs.org/1.5.10/$injector/unpr?p0=LeaveServiceProvider%20%3C-%20LeaveService (line 4674)
        static/app.min.js:4674:87
        getService@static/app.min.js:4827:40
        static/app.min.js:4679:49
        getService@static/app.min.js:4827:40
        injectionArgs@static/app.min.js:4852:69
        invoke@static/app.min.js:4874:32
        WorkFn@node_modules/angular-mocks/angular-mocks.js:3130:26
        inject@node_modules/angular-mocks/angular-mocks.js:3100:46
        test/leave.service.tests.js:55:23
        loaded@http://localhost:9876/context.js:151:17
        inject@node_modules/angular-mocks/angular-mocks.js:3097:28
        test/leave.service.tests.js:55:23
        loaded@http://localhost:9876/context.js:151:17
        TypeError: undefined is not an object (evaluating '$httpBackend.expectPOST') in test/leave.service.tests.js (line 64)
        test/leave.service.tests.js:64:16
        loaded@http://localhost:9876/context.js:151:17
        TypeError: undefined is not an object (evaluating '$httpBackend.verifyNoOutstandingExpectation') in test/leave.service.tests.js (line 114)
        test/leave.service.tests.js:114:16
        loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Linux 0.0.0): Executed 1 of 5 (1 FAILED) (skipped 4) ERROR (0.042 secs / 0.01 secs)

Karma.conf.js

const webpack = require('webpack');
const webpackConfig = require('./webpack.config.js');

module.exports = function (config) {
    config.set({
        basePath: './',
        frameworks: ['jasmine', 'mocha', 'chai'],
        files: [
            './static/app.min.js',
            'node_modules/angular-mocks/angular-mocks.js',
            {pattern: 'test/leave.service.tests.js'}
        ],
        preprocessors: {
            'test/leave.service.tests.js': ['webpack']
        },
        webpack: {
            module: webpackConfig.module,
            plugins: webpackConfig.plugins
        },
        webpackMiddleware: {
            stats: 'errors-only'
        },
        notifyReporter: {
            reportEachFailure: true,
            reportSuccess: false
        },
        plugins: [
            'karma-phantomjs-launcher',
            'karma-jasmine',
            'karma-webpack',
            'karma-mocha',
            'karma-chai'
        ],
        browsers: ['PhantomJS']
    });
};

Webpack.conf.js

const webpack = require('webpack');
const path = require('path');

module.exports = {
    entry: {
        app: './src2/app.js'
    },
    output: {
        path: path.resolve(__dirname, './static'),
        publicPath: '/static/',
        filename: 'app.min.js'
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery',
            moment: 'moment'
        })
    ],

    resolve: {
        root: path.resolve('./src2'),
        extensions: ['', '.js']
    },
    module: {
        loaders: [
            {test: /\.css$/, loader: 'style-loader!css-loader'},
            {test: /\.scss$/, loader: 'style-loader!css-loader!sass-loader'},
            {test: /\.html$/, loader: 'html-loader'},
            {test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=8192&mimetype=application/font-woff'},
            {test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=8192&mimetype=application/font-woff'},
            {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=8192&mimetype=application/octet-stream'},
            {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'},
            {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=8192&mimetype=image/svg+xml'}
        ]
    },
    devServer: {
        port: 8080,
        proxy: {
            '/api': {
                target: {
                    host: '0.0.0.0',
                    protocol: 'http:',
                    port: 8000
                }
            }
        }
    }
};

Test / congé.service.js

var chai = require('chai');
var assert = chai.assert;

describe('leave API service', function () {
    var service;
    var $httpBackend;


    beforeEach(inject(function (_$httpBackend_, LeaveService) {
        $httpBackend = _$httpBackend_;
        service = LeaveService;
    }));

    it('create(): should create a leave', function (done) {
        var foo = 'bar';

        assert.equal(foo, 'bar');
        done();
    });
});

Question

Quel est le problème ici?

Associé: Karma-webpack + angular TypeError: undefined n'est pas un objet (évaluation de '$ httpBackend.expectPOST'

1
Édouard Lopez 17 janv. 2017 à 14:02

2 réponses

Meilleure réponse

Je suis retourné à un test vraiment simple et j'ai ajouté de la complexité étape par étape jusqu'à ce qu'il échoue. La solution était de:

  1. injecter mon application angular.mock.module('app')

    beforeEach(angular.mock.module('app'));
    
  2. injecter les dépendances avec angular.mock.inject()

    beforeEach(function () {
        angular.mock.inject(function (_$httpBackend_, _LeaveService_) {
            $httpBackend = _$httpBackend_;
            service = _LeaveService_;
        });
    });
    
  3. chargez le app, le angular-mock et le test/**/*.js intégrés dans karma.conf.js:

    files: [
        './static/app.min.js',
        'node_modules/angular-mocks/angular-mocks.js',
        {pattern: 'test/foo.service.tests.js'}
    ]
    

Code complet ci-dessous

Toto.service.tests.js

var chai = require('chai');
var assert = chai.assert;

describe('leave API service', function () {
    var service;
    var $httpBackend;


    beforeEach(angular.mock.module('app'));

    beforeEach(function () {
        angular.mock.inject(function (_$httpBackend_, _LeaveService_) {
            $httpBackend = _$httpBackend_;
            service = _LeaveService_;
        });
    });

    it('create(): should create a leave', function (done) {
        var foo = 'bar';


        assert.equal(foo, 'bar');
        done();
    });
});

Webpack.conf.js

const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');

module.exports = {
        entry: {
                app: './src/app.js'
        },
        output: {
                path: path.resolve(__dirname, './static'),
                publicPath: '/static/',
                filename: '[name]-[hash:8].min.js'
        },
        plugins: [
                new webpack.optimize.ModuleConcatenationPlugin(),
                new webpack.ProvidePlugin({
                        $: 'jquery',
                        jQuery: 'jquery',
                        moment: 'moment',
                        Util: 'exports-loader?Util!bootstrap/js/dist/util'
                }),
                new HtmlWebpackPlugin({
                        template: path.resolve(__dirname, './src', 'index.html'),
                        filename: path.resolve(__dirname, 'index.html'),
                        alwaysWriteToDisk: true
                }),
                new HtmlWebpackHarddiskPlugin()
        ],
        resolve: {alias: {Services: path.resolve(__dirname, 'src/services/')}},
        module: {
                rules: [
                        {test: /\.css$/, use: ['style-loader', 'css-loader']},
                        {test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader']},
                        {test: /\.html$/, use: ['html-loader']},
                        {test: /src.*\.js$/, use: ['ng-annotate-loader']},
                        {test: /\.(png|jpg|jpeg|gif|ico)$/, loader: 'file-loader?name=[name].[ext]'},
                        {test: /\.(woff|woff2|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'}
                ]
        },
        devServer: {
                port: 8080,
                proxy: {
                        '/api': {target: 'http://0.0.0.0:8000'},
                        '/exports': {target: 'http://0.0.0.0:8000'},
                        '/media': {target: 'http://0.0.0.0:8000'}
                }
        }
};

Karma.con.js

const webpack = require('webpack');
const webpackConfig = require('./webpack.config.js');

module.exports = function (config) {
    config.set({
        basePath: './',
        frameworks: ['jasmine', 'mocha', 'chai'],
        files: [
            './static/app.min.js',
            'node_modules/angular-mocks/angular-mocks.js',
            {pattern: 'test/foo.service.tests.js'}
        ],
        preprocessors: {
            'test/foo.service.tests.js': ['webpack']
        },
        webpack: {
            module: webpackConfig.module,
            plugins: webpackConfig.plugins
        },
        webpackMiddleware: {
            stats: 'errors-only'
        },
        notifyReporter: {
            reportEachFailure: true,
            reportSuccess: false
        },
        plugins: [
            'karma-phantomjs-launcher',
            'karma-jasmine',
            'karma-webpack',
            'karma-mocha',
            'karma-chai'
        ],
        browsers: ['PhantomJS']
    });
};
0
Édouard Lopez 12 févr. 2018 à 17:08

Lorsqu'un contrôleur Angular ou un service dépend d'un autre objet Angular (service, fabrique, constante ...), l'injecteur le recherchera dans la liste des fournisseurs (un tableau de constructeurs singleton). S'il n'y a pas d'injecteur, ou si la dépendance n'existe pas, angular se fermera avec une erreur comme celle que vous avez (pas de fournisseur xProvider pour le service x). Cela devrait être résolu en se moquant du module angulaire et en lui attachant les services dépendants (méthode 1), ou en instanciant votre LeaveService avec une fonction renvoyant un objet rempli de méthodes comme argument de dépendance, comme

// leave-service.spec.js
...
var FakeHttpService = function() {
  return {
    get: function(url) { return new Promise(); },
    post: function(url, data) { return new Promise(); },
    //...
  };
};
var leaveService = new LeaveService(new FakeHttpService());

Ensuite, lorsque vous faites quelque chose comme var res = leaveService.get(url), il doit appeler la méthode get FakeHttpService.

Vous devriez lire la Documentation Angular sur $ httpBackend pour plus de détails sur les services de test qui utilisent le service $ http (méthode 1), et comprennent comment l'injecteur fonctionne dans un environnement de test.

0
Ripley511 17 janv. 2017 à 11:54