templateUrl을 사용한 단위 테스트 AngularJS 지시문


122

AngularJS 지시문이 있습니다. templateUrl정의 된 . Jasmine으로 단위 테스트를 시도하고 있습니다.

내 Jasmine JavaScript는 권장 사항에 따라 다음과 같습니다 .

describe('module: my.module', function () {
    beforeEach(module('my.module'));

    describe('my-directive directive', function () {
        var scope, $compile;
        beforeEach(inject(function (_$rootScope_, _$compile_, $injector) {
            scope = _$rootScope_;
            $compile = _$compile_;
            $httpBackend = $injector.get('$httpBackend');
            $httpBackend.whenGET('path/to/template.html').passThrough();
        }));

        describe('test', function () {
            var element;
            beforeEach(function () {
                element = $compile(
                    '<my-directive></my-directive>')(scope);
                angular.element(document.body).append(element);
            });

            afterEach(function () {
                element.remove();
            });

            it('test', function () {
                expect(element.html()).toBe('asdf');
            });

        });
    });
});

Jasmine 사양 오류에서 이것을 실행하면 다음 오류가 발생합니다.

TypeError: Object #<Object> has no method 'passThrough'

내가 원하는 것은 templateUrl이있는 그대로로드되는 것입니다 respond. 사용하고 싶지 않습니다 . 나는 이것이 사용하여 관련이있을 수 있습니다 생각 ngMock 대신 ngMockE2E을 . 이것이 범인이라면 전자 대신 후자를 어떻게 사용합니까?

미리 감사드립니다!


1
나는 .passThrough();그런 식으로 사용하지 않았지만 문서에서 다음과 같은 것을 시도해 보셨습니까 $httpBackend.expectGET('path/to/template.html'); // do action here $httpBackend.flush();? 이것이 귀하의 사용에 더 적합하다고 생각합니다. 요청을 잡기를 원하지는 whenGet()않지만 대신 전송되었는지 확인한 다음 실제로 보내?
Alex Osborn 2013 년

1
답장을 보내 주셔서 감사합니다. 나는 그것이 expectGET요청 을 보내는 것 같지 않다 ... 적어도 상자에서. 에서 문서 와의 예는 /auth.py$httpBackend.when받는 이전 $httpBackend.expectGET$httpBackend.flush전화.
자레드

2
맞습니다 expectGet. 요청이 시도되었는지 확인하는 것입니다.
Alex Osborn 2013 년

1
아. 글쎄요 $httpBackend, 아래의 지시문에 제공된 URL을 실제로 사용하고 가져 오라고 모의에게 지시 하는 방법이 필요 templateUrl합니다. 나는 passThrough이것을 할 것이라고 생각했다 . 이 작업을 수행하는 다른 방법을 알고 있습니까?
Jared

2
흠, 아직 e2e 테스트를 많이하지 않았지만 문서를 확인하고 있습니다-대신 e2e 백엔드를 사용해 보셨나요
Alex 오스본

답변:


187

ngMock과 관련이 있다는 것이 맞습니다. ngMock 모듈은 모든 Angular 테스트에 대해 자동으로로드 되며 템플릿 가져 오기를 포함 $httpBackend하는 $http서비스 사용을 처리하기 위해 모의 를 초기화합니다 . 템플릿 시스템은 템플릿을로드하려고 시도 $http하고 모의 객체에 대한 "예기치 않은 요청"이됩니다.

$templateCacheAngular가를 사용하지 않고 템플릿을 요청할 때 이미 사용할 수 있도록 템플릿을에 미리로드하는 방법이 필요합니다 $http.

선호하는 솔루션 : Karma

Karma 를 사용 하여 테스트를 실행 하는 경우 (그리고 그래야만한다면) ng-html2js 전처리 기로 템플릿을로드하도록 구성 할 수 있습니다 . Ng-html2js는 사용자가 지정한 HTML 파일을 읽고 .NET Framework를 미리로드하는 Angular 모듈로 변환합니다 $templateCache.

1 단계 : 컴퓨터에서 전처리기를 활성화하고 구성합니다. karma.conf.js

// karma.conf.js

preprocessors: {
    "path/to/templates/**/*.html": ["ng-html2js"]
},

ngHtml2JsPreprocessor: {
    // If your build process changes the path to your templates,
    // use stripPrefix and prependPrefix to adjust it.
    stripPrefix: "source/path/to/templates/.*/",
    prependPrefix: "web/path/to/templates/",

    // the name of the Angular module to create
    moduleName: "my.templates"
},

Yeoman 을 사용 하여 앱을 스캐 폴딩하는 경우이 구성이 작동합니다.

plugins: [ 
  'karma-phantomjs-launcher', 
  'karma-jasmine', 
  'karma-ng-html2js-preprocessor' 
], 

preprocessors: { 
  'app/views/*.html': ['ng-html2js'] 
}, 

ngHtml2JsPreprocessor: { 
  stripPrefix: 'app/', 
  moduleName: 'my.templates' 
},

2 단계 : 테스트에서 모듈 사용

// my-test.js

beforeEach(module("my.templates"));    // load new module containing templates

전체 예제를 보려면 Angular 테스트 전문가 Vojta Jina의 표준 예제를 살펴보십시오 . 여기에는 카르마 구성, 템플릿 및 테스트와 같은 전체 설정이 포함됩니다.

카르마가 아닌 솔루션

어떤 이유로 든 Karma를 사용하지 않고 (레거시 앱에서 융통성없는 빌드 프로세스가 있었음) 브라우저에서 테스트 $httpBackend하는 경우 원시 XHR을 사용하여 실제 템플릿을 가져옴으로써 ngMock의 인수를 피할 수 있음을 발견했습니다. 에 삽입합니다 $templateCache. 이 솔루션은 훨씬 덜 유연하지만 지금은 작업이 완료됩니다.

// my-test.js

// Make template available to unit tests without Karma
//
// Disclaimer: Not using Karma may result in bad karma.
beforeEach(inject(function($templateCache) {
    var directiveTemplate = null;
    var req = new XMLHttpRequest();
    req.onload = function() {
        directiveTemplate = this.responseText;
    };
    // Note that the relative path may be different from your unit test HTML file.
    // Using `false` as the third parameter to open() makes the operation synchronous.
    // Gentle reminder that boolean parameters are not the best API choice.
    req.open("get", "../../partials/directiveTemplate.html", false);
    req.send();
    $templateCache.put("partials/directiveTemplate.html", directiveTemplate);
}));

심각하게 생각. Karma를 사용하십시오 . 설정하는 데 약간의 작업이 필요하지만 명령 줄에서 한 번에 여러 브라우저에서 모든 테스트를 실행할 수 있습니다. 따라서이를 지속적 통합 시스템의 일부로 사용하거나 편집기에서 바로 가기 키로 만들 수 있습니다. alt-tab-refresh-ad-infinitum보다 훨씬 낫습니다.


6
이것은 분명 할 수 있지만 다른 사람들이 같은 일에 갇혀서 여기에서 답을 찾아 보면 : preprocessors파일 패턴 (예 :) "path/to/templates/**/*.html"files섹션에 추가하지 않고는 작동 할 수 없습니다 karma.conf.js.
Johan

1
그렇다면 계속하기 전에 응답을 기다리지 않는 데 중요한 문제가 있습니까? 요청이 돌아 오면 값만 업데이트됩니까 (IE는 30 초 소요)?
Jackie

1
@Jackie falseXHR open호출에 대한 매개 변수를 사용하여 동기식으로 만드는 "비 Karma"예제에 대해 이야기하고 있다고 가정합니다 . 그렇게하지 않으면 템플릿을로드하지 않고도 실행이 계속되고 테스트 실행이 시작됩니다. 그러면 동일한 문제로 돌아갈 수 있습니다. 1) 템플릿 요청이 종료됩니다. 2) 테스트가 시작됩니다. 3) 테스트는 지시문을 컴파일하고 템플릿은 여전히로드되지 않습니다. 4) Angular $http는 모의 된 서비스를 통해 템플릿을 요청합니다 . 5) 모의 $http서비스가 "예기치 않은 요청"이라고 불평합니다.
SleepyMurph 2014 년

1
나는 Karma없이 grunt-jasmine을 실행할 수 있었다.
FlavorScape 2014 년

5
또 다른 것 : karma-ng-html2js-preprocessor ( npm install --save-dev karma-ng-html2js-preprocessor) 를 설치 karma.conf.js하고 stackoverflow.com/a/19077966/859631 에 따라 의 플러그인 섹션에 추가해야합니다 .
Vincent

37

내가 한 일은 템플릿 캐시를 가져와 거기에 뷰를 넣는 것입니다. 나는 ngMock을 사용하지 않는 것에 대한 통제권이 없습니다.

beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) {
    $scope = _$rootScope_;
    $compile = _$compile_;
    $templateCache.put('path/to/template.html', '<div>Here goes the template</div>');
}));

26
이 방법에 대한 제 불만은 ... 이제 우리가 템플릿 캐시에 문자열로 주입 할 큰 html 조각을 가지게된다면 프런트 엔드에서 html을 변경할 때 무엇을해야할까요? ? 테스트에서도 html을 변경 하시겠습니까? 지속 불가능한 대답 인 IMO와 templateUrl 옵션을 통해 템플릿을 사용한 이유입니다. 비록 내가 내 html을 지시문에서 거대한 문자열로 사용하는 것을 매우 싫어하지만, 두 곳의 html을 업데이트 할 필요가없는 가장 지속 가능한 솔루션입니다. 시간이 지남에 따라 HTML이 일치하지 않을 수있는 많은 이미징이 필요하지 않습니다.
Sten Muchow 2014

12

이 초기 문제는 다음을 추가하여 해결할 수 있습니다.

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

기본적으로 ngMock 모듈 에서 $ httpBackend 를 찾으려고 하는데 꽉 차지 않았기 때문 입니다.


1
글쎄, 그것은 실제로 원래 질문에 대한 정답입니다 (그것이 나를 도왔습니다).
Mat

이것을 시도했지만 passThrough ()는 여전히 나를 위해 작동하지 않았습니다. 여전히 "예기치 않은 요청"오류가 발생했습니다.
frodo2975 2015

8

내가 도달 한 솔루션에는 jasmine-jquery.js와 프록시 서버가 필요합니다.

나는 다음 단계를 따랐다.

  1. karma.conf에서 :

파일에 jasmine-jquery.js 추가

files = [
    JASMINE,
    JASMINE_ADAPTER,
    ...,
    jasmine-jquery-1.3.1,
    ...
]

조명기를 서버 할 프록시 서버를 추가하십시오.

proxies = {
    '/' : 'http://localhost:3502/'
};
  1. 귀하의 사양

    describe ( 'MySpec', function () {var $ scope, template; jasmine.getFixtures (). fixturesPath = 'public / partials /'; // 앱에서 사용하는 실제 템플릿을 제공 할 수있는 사용자 지정 경로 beforeEach (function () {템플릿 = angular.element ( '');

        module('project');
        inject(function($injector, $controller, $rootScope, $compile, $templateCache) {
            $templateCache.put('partials/resources-list.html', jasmine.getFixtures().getFixtureHtml_('resources-list.html')); //loadFixture function doesn't return a string
            $scope = $rootScope.$new();
            $compile(template)($scope);
            $scope.$apply();
        })
    });

    });

  2. 앱의 루트 디렉터리에서 서버 실행

    파이썬 -m SimpleHTTPServer 3502

  3. 업장을 실행하십시오.

많은 게시물을 검색해야했기 때문에이 문제를 파악하는 데 시간이 걸렸습니다. 매우 중요한 문제이므로 이에 대한 문서가 더 명확해야한다고 생각합니다.


자산을 제공 localhost/base/specs하고 프록시 서버를 python -m SimpleHTTPServer 3502실행 하여 추가하는 데 문제가 있었습니다. 당신은 천재입니다!
pbojinov

내 테스트에서 $ compile에서 빈 요소가 반환되었습니다. 다른 곳에서는 $ scope. $ digest () 실행을 제안했습니다. 여전히 비어 있습니다. $ scope. $ apply () 실행은 작동했습니다. 지시문에서 컨트롤러를 사용하고 있기 때문이라고 생각합니까? 확실하지 않다. 충고 감사합니다! 도왔습니다!
Sam Simmons

7

내 솔루션 :

test/karma-utils.js:

function httpGetSync(filePath) {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/base/app/" + filePath, false);
  xhr.send();
  return xhr.responseText;
}

function preloadTemplate(path) {
  return inject(function ($templateCache) {
    var response = httpGetSync(path);
    $templateCache.put(path, response);
  });
}

karma.config.js:

files: [
  //(...)
  'test/karma-utils.js',
  'test/mock/**/*.js',
  'test/spec/**/*.js'
],

시험:

'use strict';
describe('Directive: gowiliEvent', function () {
  // load the directive's module
  beforeEach(module('frontendSrcApp'));
  var element,
    scope;
  beforeEach(preloadTemplate('views/directives/event.html'));
  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
  }));
  it('should exist', inject(function ($compile) {
    element = angular.element('<event></-event>');
    element = $compile(element)(scope);
    scope.$digest();
    expect(element.html()).toContain('div');
  }));
});

개발자가 Karma를 사용하도록 강요하지 않는 첫 번째 괜찮은 솔루션입니다. 왜 앵귤러 맨들은 멋진 일 가운데서 그렇게 나쁘고 쉽게 피할 수있는 일을할까요? pfff
Fabio Milheiro 2014

나는 당신이 'test / mock / ** / *. js'를 추가하고 서비스와 같은 모든 모의 물건을로드한다고 가정합니다. 모의 서비스의 코드 중복을 피하는 방법을 찾고 있습니다. 그것에 대해 좀 더 보여 주시겠습니까?
Stephane

정확히 기억하지 못하지만 $ http 서비스에 대한 예를 들어 JSON과 같은 설정이있을 수 있습니다. 멋진 것은 없습니다.
bartek

오늘이 문제가 있었다-훌륭한 솔루션. 우리는 카르마를 사용하지만 Chutzpah도 사용합니다. 우리가 카르마를 사용하도록 강요 받아야 할 이유는없고 오직 카르마 만 단위 테스트 지시문을 사용할 수 있도록해야합니다.
lwalden 2015

우리는 Angular와 함께 Django를 사용하고 있는데, 이것은 templateUrl을로드하는 지시문을 테스트하는 매력처럼 작동했습니다 static. 예를 들어 beforeEach(preloadTemplate(static_url +'seed/partials/beChartDropdown.html')); Thanks!
Aleck Landgraf 2015 년

6

Grunt를 사용하는 경우 grunt-angular-templates를 사용할 수 있습니다. 템플릿을 templateCache에로드하고 사양 구성에 투명합니다.

내 샘플 구성 :

module.exports = function(grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    ngtemplates: {
        myapp: {
          options: {
            base:       'public/partials',
            prepend:    'partials/',
            module:     'project'
          },
          src:          'public/partials/*.html',
          dest:         'spec/javascripts/angular/helpers/templates.js'
        }
    },

    watch: {
        templates: {
            files: ['public/partials/*.html'],
            tasks: ['ngtemplates']
        }
    }

  });

  grunt.loadNpmTasks('grunt-angular-templates');
  grunt.loadNpmTasks('grunt-contrib-watch');

};

6

선택한 솔루션과 약간 다른 방식으로 동일한 문제를 해결했습니다.

  1. 먼저 karma 용 ng-html2js 플러그인을 설치하고 구성했습니다 . karma.conf.js 파일에서 :

    preprocessors: {
      'path/to/templates/**/*.html': 'ng-html2js'
    },
    ngHtml2JsPreprocessor: {
    // you might need to strip the main directory prefix in the URL request
      stripPrefix: 'path/'
    }
  2. 그런 다음 beforeEach에서 만든 모듈을로드했습니다. Spec.js 파일에서 :

    beforeEach(module('myApp', 'to/templates/myTemplate.html'));
  3. 그런 다음 $ templateCache.get을 사용하여 변수에 저장했습니다. Spec.js 파일에서 :

    var element,
        $scope,
        template;
    
    beforeEach(inject(function($rootScope, $compile, $templateCache) {
      $scope = $rootScope.$new();
      element = $compile('<div my-directive></div>')($scope);
      template = $templateCache.get('to/templates/myTemplate.html');
      $scope.$digest();
    }));
  4. 마지막으로 이런 방식으로 테스트했습니다. Spec.js 파일에서 :

    describe('element', function() {
      it('should contain the template', function() {
        expect(element.html()).toMatch(template);
      });
    });

4

템플릿 html을 $ templateCache에 동적으로로드하려면 여기에 설명 된대로 html2js karma 전처리기를 사용할 수 있습니다.

이것은 conf.js 파일과 전 처리기 = { '의 파일에 템플릿' .html ' 을 추가하는 것으로 귀결됩니다. .html': 'html2js'};

그리고 사용

beforeEach(module('..'));

beforeEach(module('...html', '...html'));

js 테스트 파일에


나는 무엇입니까Uncaught SyntaxError: Unexpected token <
Melbourne2991

2

Karma를 사용하는 경우 karma-ng-html2js- 전처리기를 사용하여 외부 HTML 템플릿을 사전 컴파일하고 Angular가 테스트 실행 중에 HTTP GET을 시도하지 않도록하십시오. 앱 대 테스트 디렉토리 구조의 차이로 인해 테스트 중이 아닌 일반적인 앱 실행 중에 해결 된 templateUrl의 부분 경로와 같은 몇 가지 문제를 해결했습니다.


2

당신이 사용하는 경우 자스민 - 받는다는 - 플러그인 RequireJS와 함께 당신이 사용할 수있는 텍스트 플러그인을 변수로 템플릿 내용을로드 한 후 템플릿 캐시에 넣어.


define(['angular', 'text!path/to/template.html', 'angular-route', 'angular-mocks'], function(ng, directiveTemplate) {
    "use strict";

    describe('Directive TestSuite', function () {

        beforeEach(inject(function( $templateCache) {
            $templateCache.put("path/to/template.html", directiveTemplate);
        }));

    });
});

카르마없이 이것을 할 수 있습니까?
Winnemucca

2

테스트에서 requirejs를 사용하는 경우 'text'플러그인을 사용하여 html 템플릿을 가져와 $ templateCache에 넣을 수 있습니다.

require(["text!template.html", "module-file"], function (templateHtml){
  describe("Thing", function () {

    var element, scope;

    beforeEach(module('module'));

    beforeEach(inject(function($templateCache, $rootScope, $compile){

      // VOILA!
      $templateCache.put('/path/to/the/template.html', templateHtml);  

      element = angular.element('<my-thing></my-thing>');
      scope = $rootScope;
      $compile(element)(scope);   

      scope.$digest();
    }));
  });
});

0

모든 템플릿을 templatecache로 컴파일하여이 문제를 해결합니다. 나는 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 같은 솔루션을 찾을 수 있습니다. 지시문의 내 templateUrls, 모달은 다음과 같습니다.

`templateUrl: '/templates/directives/sidebar/tree.html'`
  1. 내 package.json에 새 npm 패키지 추가

    "gulp-angular-templatecache": "1.*"

  2. gulp 파일에서 templatecache 및 새 작업을 추가하십시오.

    var templateCache = require('gulp-angular-templatecache'); ... ... gulp.task('compileTemplates', function () { gulp.src([ './app/templates/**/*.html' ]).pipe(templateCache('templates.js', { transformUrl: function (url) { return '/templates/' + url; } })) .pipe(gulp.dest('wwwroot/assets/js')); });

  3. index.html에 모든 js 파일 추가

    <script src="/assets/js/lib.js"></script> <script src="/assets/js/app.js"></script> <script src="/assets/js/templates.js"></script>

  4. 즐겨!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.