Lazy loaded modules for Jasmine tests. Angularjs

У меня есть несколько lazy loaded модулей в моей angularjs приложении. Но эти динамически подгруженные модули я не могу тестировать unit-тестами jasmin, так как в момент прогонки тестов эти модули не подгруженны. Как их подгрузить я без понятия в доках этого пакета нет вообще никакой инфы https://oclazyload.readme.io/docs/unit-tests

А то что я нашел в сети у меня не работает ¯\(ツ)

Есть здесь знающие хорошо angularjs и как он работает с lazy loaded модулями?
Отвечу на все уточняющие вопросы)

my karma config:

config.set({

   plugins: [

       require('karma-babel-preprocessor'),

       require('karma-chrome-launcher'),

       require('karma-jasmine'),

       require('karma-phantomjs-launcher'),

       require('karma-safari-launcher'),

       require('karma-spec-reporter'),

   ],

   preprocessors: {

       'app/**/*.spec.js': ['babel'],

       'app/components/my.lazy.module.js': ['babel']

   },

   babelPreprocessor: {

       options: {

           presets: ['es2015'],

           plugins: ['syntax-dynamic-import']

       }

   },

   files: [

       'app/dest/app.js',

       'app/dest/*.bundle.js',

       'app/components/my.lazy.module.js',

       'node_modules/angular-mocks/angular-mocks.js',

       'node_modules/babel-polyfill/dist/polyfill.js',

       'app/**/*.spec.js'

   ],


    frameworks: [

       'jasmine'

   ],


    reporters: [

       'spec'

   ],
I tried load it something like that:

beforeEach(inject(($ocLazyLoad) => {

   import(/* webpackChunkName: "my.lazy" */ 'base/app/components/my.lazy.module.js')

       .then(mod => {

           return $ocLazyLoad.inject(mod.MyLazyModule);

       })

 }));

I tried load it something like that:

beforeEach(inject(($ocLazyLoad) => {

   import(/* webpackChunkName: "my.lazy" */ 'base/app/components/my.lazy.module.js')

       .then(mod => {

           return $ocLazyLoad.inject(mod.MyLazyModule);

       })

 }));

Ну или подскажите в каком направлении думать и двигаться)

Паравшишь в интернетах и учитывая эрроры которые вылетают, я решил не юзать файлы из dest, а билдить их прямо внутри конфига для karma

получился конфиг кармы такой

    var path = require('path');
var karmaWebpack = require('karma-webpack');
var webpackConfig = require('./MaterialApp/webpack-config/local.config.js');
var entry = path.resolve(webpackConfig.context, webpackConfig.entry.app);
var preprocessors = {};
preprocessors[entry] = ['webpack'];
preprocessors['MaterialApp/**/*.spec.js'] = ['babel'];
preprocessors['MaterialApp/components/team/team-member/team-member.module.js'] = ['webpack'];

module.exports = function (config) {
    config.set({
        plugins: [
            require('karma-babel-preprocessor'),
            require('karma-chrome-launcher'),
            require('karma-jasmine'),
            require('karma-phantomjs-launcher'),
            require('karma-safari-launcher'),
            require('karma-spec-reporter'),
            require('karma-webpack')
        ],
        preprocessors: preprocessors,
        babelPreprocessor: {
            options: {
                presets: ['es2015'],
                plugins: ['syntax-dynamic-import']
            }
        },
        webpack: webpackConfig,
        files: [
            'node_modules/angular/angular.min.js',
            'node_modules/angular-mocks/angular-mocks.js',
            entry,
            'MaterialApp/components/team/team-member/team-member.module.js',
            'node_modules/babel-polyfill/dist/polyfill.js', // fix PhantomJS: "undefined is not a function (evaluating 'Object.assign(...)"
            'MaterialApp/**/*.spec.js'
        ],
        frameworks: [ 'jasmine' ],
        reporters: [ 'spec' ],
        specReporter: {
            maxLogLines: 5,
            suppressErrorSummary: true,
            suppressFailed: false,
            suppressPassed: false,
            suppressSkipped: false,
            showSpecTiming: true,
            failFast: false
        },
        //logLevel: config.LOG_DEBUG,
        singleRun: false,
        client: {
            captureConsole: false
        }
    });
};

а внутри спеки я пытаюсь заимпортить модуль так

    beforeEach(inject(($ocLazyLoad) => {
import(/* webpackChunkName: "team.members.member" */ '/base/MaterialApp/components/team/team-member/team-member.module.js')
    .then(mod => {
        return $ocLazyLoad.inject(module('replyApp').TeamMemberModule);
    })
    .catch(err => {
        throw new Error("Ooops, something went wrong, " + err);
    });

}));

вылетает такой эррор

1 лайк

Я так и не смог внутри Jasmine тестов проинить модуль, как я это делаю в роут конфигах приложения
И смог сбилдить все вебпаком внутри кармы, но это тоже не помогло.
Вероятно нужно как из тестов тригерить какую-то функцию/сервис в приложении, а уже она будет делать динамический импорт и все такое

Ниже кусок, где я юзаю динамический импорт в роут конфиге

1 лайк

Если кому не сложно апвоутните мой вопрос на стакеовервлоу) Может кто-то заинтересуется

Я не сталкивался именно с ленивыми модулями, но в jasmine работал.

Думаю подход через beforeEach - верное направление. Единственное что beforeEach по умолчанию выполняется синхронно, следовательно jasmine запускает тесты до того как все асинхронные операции в beforeEach завершены. Попробуй подтягивать зависимости в beforeEach в асинхронном стиле (т.е. объявляя коллбек с аргументом done. В него jasmine передаст функцию которую нужно вызвать когда все асинхронные операции из beforeEach завершатся).

beforeEach(function(done) {
	inject(($ocLazyLoad) => {
	   import(/* webpackChunkName: "my.lazy" */ 'base/app/components/my.lazy.module.js')
	       .then(mod => {
	       		setTimeout(done, 10)
	           return $ocLazyLoad.inject(mod.MyLazyModule);
	       })
	 })
});
1 лайк

Вот к какому выводу я пришел)

Что бы корректно отработал динамический импорт мне нужно файл, в котором он указан, сбилдить вебпаком. Но сделать я этого не могу т.к. вебпак ругается на кармовский beforeEach(module('module'));

я решил сделать так: заинжектить внутрь .run() при ините приложения, сервис в котором будут инициализироваться динамические импорты

И я хочу что бы в зависимости от билда (для тестов или для энвов) внутри сервиса соответствующий код билдился

class DynamicImportsForTests {
constructor($transition$) {
    'ngInject';

    this.$transition$ = $transition$
}

init(str) {
    if (env.UNIT_TESTS) {
        const $ocLazyLoad = this.$transition$.injector().get('$ocLazyLoad');

        import(/* webpackChunkName: "team.members.member" */ '../../components/team/team-member/team-member.module.js')
            .then(mod => {
                return $ocLazyLoad.inject(mod.TeamMemberModule);
            })
            .catch(err => {
                throw new Error("Ooops, something went wrong, " + err);
            });
    } else {
        return;
    }
}
}

export default DynamicImportsForTests;

Типа раньше динамические импорты иницилизировались в конфигах роутера. В резолве определенной урлы инитился соответствующий импорт. А для тестов я хочу сделать так, что бы все динамические импорты отработали почти как и обычные. И тогда вероятно в момент прогонки тестов нужные модули уже будут подгружены

Возможно это? Что бы в зависимости от env.UNIT_TESTS в бандле был нужный сервис?

В целом звучит очень хрупко. Давай попробуем подойти к проблеме издалека. Почему ты не билдишь .speck.js вебпаком?

preprocessors['MaterialApp/**/*.spec.js'] = ['babel'];

теоретически он должен разобраться со всеми этими динамическими импортами. Процесс сборки не должен отличаться для тестов и для продакшена. Пайплайн должен быть единственным - в твоем случае пропущенным через webpack.

И бабели тоже не нужны в конфиге кармы.

Ну бабелями я прогоняю спеки тестов т.к. я хочу что бы ес6 был доступен в тестах.

А вебпаком не могу билдить спеки, так как он реагирует на жасминовские module() и выдает ошибки

Попробуй это решение проблемы с module not a function.

Собирать код для тестов нужно так же как и для разработки/продакшена. Иначе у тебя со временем код будет “расползатся”, ломаясь тов в одном то в другом из-за того что тебе придется поддерживать поддерживать 2 системы сборки (для разработки и для тестов).

Я пришел к выводу, что через тесты инициализировать загрузку ленивых модулей нельзя. Для это-го сам модуль и его динамический импорт должны быть в одном бандле. Иначе динамический импорт не запоминает название бандла в который должен грузить свой ленивый модуль.

Через тесты реализовать это не получится, ну или по крайней мере у меня это не получается. Возможно все же где-то у меня затык)

Попробую это сделать в приложении. Инициировать динамические модули внутри приложения в зависимости от вебпак конфига.

1 лайк

Небольшая победа!)

С помощью небольшого изменения в вебпак конфиге, мне удалось в зависимости от process.env.NODE_ENV собирать приложение с уже проинициализированными динамическими модулями

При сборке приложения для браузера я могу это настроить) И могу получить в браузере бандл с подргружеными ленивыми модулями и наоборот.

Но при запуске карма конфига, я не могу заставить подгрузить чанки для этих ленивых модулей, как быть?)


download%20(1)

1 лайк

Вариант с require-ом в beforeEach не срабатывает?

Вариант проинитить модуль в самой тест спеке не подходит. Так как само объявление и инициализация ленивого модуля должны быть в одном бандле, иначе чанки грузятся но не запоминают свои названия.

Не могу никак разобраться, как сказать карме, что нужно кроме главного бандла еще и чанки себе в тестовое окружение грузить?

Попробуй писать сюда, там ангулярщики общаются…

Кто хочет увидеть нагляднее все, я тут скрафтил демку с поведеним ленивых модулей и тестов для них

Артем, мне кажется ты не туда роешь. Такое ощущение что ты юзаешь реальные зависимости в тестах - это в корне неправильно. Все зависимости должны мокатся. И грузить что то, импортить в юнит тесте - звучит крайне странно.
https://angular.io/guide/testing#angular-testbed

1 лайк

Ты имеешь ввиду uiRouter, oclazyload в ключевом модуле? По другому построить архитектуру как-то следует?

Но мне кажется меня не правильно поняли. В момент прогонки карма конфига, я гружу в среду где отрабатывают тесты dist/app.js, dist/*.bundle.js, то, что у меня выплюнул вебпак после сборки.
Ключевой бандл и бандлы с айдишниками для ленивых модулей.

Внутри карма спеки я через angular.mock.module(‘demoApp’) создаю модуль для теста, а затем так же через angular.mock создаю $componentController(‘defaultFirst’, {}, {});
И на этом этапе у контроллера компонента нет никаких зависимостей, есть только зависимости в ключевом модуле, это я про uiRouter, oclazyload, я полагал, что они мокаются, через ангуляр мок

Но… допустим, что созданный модуль в тестах использует реальные зависимости в тесте, но как мок зависимостей поможет мне ленивый модуль подгрузить?

Поправьте меня, если я в чем-то ошибаюсь.

Ключевая проблема в сборке, что я сделал, это то что, в момент отработки спеки, я использую готовый бандл от вебпака, в котором нет того модуля, который я собрался тестировать.

Самое близкое и правильное на мой взгляд решение было у меня это добавить в общий бандл кусок кода, который в зависимости от вебпак конфига будет собирать бандл С и БЕЗ ленивых модулей. Что собсно я и сделал в dynamic-imports-for-tests.module.js

В браузере это работает, модули подгружены сразу, общий бандл с чанк бандлами связаны и ничего не подгружается уже. НО в спеке это не работает, ангуляр мок не понимает такой ленивый модуль

Возможно изначально конфиг кармы я неправильно настроил? Может собирать приложение внутри карма конфига, через вебпак? Я так тоже пробовал, и сутация такая-же

И приложение и тесты у меня на angularjs, я не понял как использовать все прелести и фичи из линки

Сделал небольшой апдейт

Так как механика ленивых модулей и oclazyload либы немного отличается от дефолтной, ангуляр моки не умеют в лениво подгруженные модули.
По этому идея с сервисом который в зависимости от вебпаковской NODE_ENV инитил и не инитил ленивые модули, прогорела.

Я удалил сервис и добавил две разные точки входа app.build для дева и прод билда и app.unit, которые соответственно импортят нужный им роут конфиг rout-config.build и rout-config.unit

Да, вариант так себе, так как нужно поддерживать актуальность почти дублирующих файлов, но зато работает, и можно не отказываться от ленивых модулей в angularjs из-зи тестов на карме.

Если есть какие-то идеи, как это можно улучшить, то вперед

Возможно можно скрафтить свой вебпаковский лоадер, который будет лоадить файл и парсить его в нужном виде. Тип один вариант с модифицированным конфигом и без зависимостей в точке входа и наоборот. Но в лоадерах и ноде я пока еще не силен.

тут все еще можно смотреть упрощенный сетап для этой проблемы.

Раунд!)

1 лайк