Angular 2: "Неизвестный поставщик: $$ angularInjectorProvider" при модульном тестировании с пониженной службой "UpgradeModule"

В настоящее время наше приложение содержит около 4500 + существующих тестов Angular 1. Теперь мы включили Angular 2, используя UpgradeModule, чтобы понизить уровень сервиса Angular 2, который используется в нашем методе работы(). Это приводит к сбою всех тестов с ошибкой Error: [$injector:unpr] Unknown Provider $$angularInjectorProvider.

Я создал plnkr, чтобы продемонстрировать эту проблему (http://plnkr.co/edit/XuMYBr9xInqq18Kr6EAs?p=preview).

В основном модуль Angular 1 выглядит следующим образом:

// angular1.module.ts

angular.module( 'testApp' )
    .run( [ 'Angular2Service', Angular2Service => {  // injecting downgraded service
        Angular2Service.showTestMessage();
    } ] );

Где Angular2Service понижается:

// app.module.ts

angular.module( 'testApp' )
    .factory( 'Angular2Service', downgradeInjectable( Angular2Service ) );


@NgModule({
    imports: [ BrowserModule, UpgradeModule ],
    providers : [ Angular2Service ]
})
export class AppModule {
    ngDoBootstrap() {}
}

Изначально с этим до сих пор я получал ошибку Неизвестного поставщика для самого 'Angular2Service' при выполнении тестов, поэтому я добавил beforeEach(), который компилирует все в AppModule:

beforeEach( async( () => {
    TestBed.configureTestingModule( {
        imports : [ AppModule ]
    } )
        .compileComponents();
} ) );

И вот где я нахожусь с ошибкой Error: [$injector:unpr] Unknown Provider $$angularInjectorProvider.

Выполнение самого теста - это просто тест Angular 1, который проходит, если я не введу Angular2Service в метод run():

describe( 'testComponent', () => {
    var $compile,
        $scope;

    beforeEach( module( 'testApp' ) );

    beforeEach( inject( $injector => {
        $compile = $injector.get( '$compile' );
        $scope = $injector.get( '$rootScope' ).$new();
    } ) );


    it( 'should show the text "Test Component" in the DOM', () => {
        var element = $compile( '<test-component></test-component>' )( $scope );
        $scope.$apply();

        expect( element[ 0 ].innerHTML ).toBe( 'Test Component' );
    } );

} );

Я надеюсь, что это может быть исправлено, так что все наши чистые тесты Angular 1 могут продолжать работать без изменений, но при необходимости я могу их обновить. Любая помощь по этому поводу будет очень благодарна!

http://plnkr.co/edit/XuMYBr9xInqq18Kr6EAs?p=preview

Ответ 1

Я столкнулся с той же проблемой и пришел с решением. Это не лучшее, что можно сделать, но оно просто решает проблему на этапе миграции.

Я уверен, что вы используете upgrade-static.umd.js dist файл, а не upgrade.umd.js. Статический предназначен для использования с компиляцией ngc AoT, которую мы должны использовать в производственных приложениях. Если вы вникнете в свой код, вы можете легко понять, что он определяет новый модуль angularjs с именем $$UpgradeModule, который регистрирует поставщик значений с именем $$angularInjector (тот, который указан в вышеприведенной ошибке) - эта вещь $$angularInjector отвечает за инъекцию модулей Angular в угловые.

Проблема

Ваш старый тест angularjs не инициирует $$UpgradeModule, поэтому его поставщики не инъектируются. Обратите внимание: angular-mocks (который определяет как глобальные module, так и inject fns) выполняет некоторые исправления для жасмина и mocka, чтобы обеспечить некоторую инъекционную изоляцию между наборами.

Решение

Вам просто нужно вызвать module('$$UpgradeModule') в первом перед каждым каждый раз, когда вы проверите код, который будет вводить (прямо или косвенно) пониженный поставщик.

Однако это не так просто:). Фактически $$UpgradeModule объявляется в функции UpgradeModule.boorstrap. это означает, что перед любым тестированием (или в более строгой форме - начальной загрузкой корневого модуля) необходимо загружать все приложение (как angularjs, так и Angular)). Это не очень приятно, потому что мы хотим протестировать модули и провайдеры в полной изоляции.

Другое решение

Альтернативой может быть использование UpgradeAdapter, определенного в upgrade.umd.js. Честно говоря, я не пробовал - но из кода кажется, что он может инициировать только те модули, которые мы хотим, не загружая все приложение. Но я думаю, что нам может потребоваться повторить понижение в тестах с помощью методов UpgradeAdapter, чтобы все могло работать.


ИЗМЕНИТЬ

Пример:

Для загрузки, вам необходимо импортировать свой модуль приложения SystemJs перед импортом тестовых файлов. Итак, ur karma-stest-shim.js будет содержать что-то вроде этого

System.import('systemjs.config.js')
  .then(importSystemJsExtras)
  .then(importApp)
  .then(initTestBed)
  .then(initTesting);

function importApp() {
    return Promise.all(
        [
            System.import('app'),
        ]
    )
}

Это сделает нормальный бутстрап для обеих версий и определит $$UpgradeModule.

Затем в любом тесте, который будет вводить пониженный поставщик

beforeEach(function () {
    // the ng1 module for the downgraded provider 
    module('services');
    // the trick is here
    module('$$UpgradeModule');
});