TemplateUrl ile Birim Testi AngularJS yönergesi


122

templateUrlTanımlı bir AngularJS direktifim var . Jasmine ile birlikte test etmeye çalışıyorum.

Benim Yasemin JavaScript önerisi başına, aşağıdaki gibi görünüyor bu :

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');
            });

        });
    });
});

Bunu Jasmine spec hatamda çalıştırdığımda aşağıdaki hatayı alıyorum:

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

Tek istediğim templateUrl'nin olduğu gibi yüklenmesi - kullanmak istemiyorum respond. Bunun ngMockE2E yerine ngMock kullanılmasıyla ilgili olabileceğine inanıyorum . Suçlu buysa, birincisi yerine ikincisini nasıl kullanırım?

Şimdiden teşekkürler!


1
Ben .passThrough();bu şekilde kullanmadım, ancak belgelerden şöyle bir şey denediniz mi: $httpBackend.expectGET('path/to/template.html'); // do action here $httpBackend.flush();Sanırım bu sizin kullanımınıza daha uygun - isteği yakalamak istemiyorsunuz, yani whenGet()bunun yerine gönderildiğini kontrol edin ve sonra aslında gönder?
Alex Osborn

1
Cevap için teşekkürler. Bunun expectGETistekleri gönderdiğini sanmıyorum ... en azından kutudan çıkar. Gelen dokümanlar ile örnek /auth.pybir sahiptir $httpBackend.whenöncesinde $httpBackend.expectGETve $httpBackend.flushçağrılar.
Jared 13

2
Bu doğru, expectGetsadece bir isteğin denenip denenmediğini kontrol etmektir.
Alex Osborn

1
Ah. Pekala, $httpBackendtaklide direktifte sağlanan URL'yi gerçekten kullanmasını ve onu almasını söylemenin bir yolunu bulmalıyım templateUrl. Bunu passThroughyapacağını düşündüm . Bunu yapmanın farklı bir yolunu biliyor musunuz?
Jared

2
Hmm, henüz çok fazla e2e testi yapmadım, ancak dokümanları kontrol ediyorum - bunun yerine e2e arka ucunu kullanmayı denediniz mi - bu yüzden passThrough yönteminiz olmadığını düşünüyorum - docs.angularjs.org/api/ngMockE2E.$httpBackend
Alex Osborn

Yanıtlar:


187

Bunun ngMock ile ilgili olduğu konusunda haklısınız. NgMock modülü, her Angular testi için otomatik olarak yüklenir ve şablon getirmeyi de içeren hizmetin $httpBackendherhangi bir kullanımını gerçekleştirmek için taklidi başlatır $http. Şablon sistemi şablonu yüklemeye çalışır $httpve bu, sahte için "beklenmedik bir istek" haline gelir.

Şablonları önceden yüklemenin bir yoluna ihtiyacınız olan $templateCacheşey, Angular kullanmadan bunları istediğinde zaten kullanılabilir olmaları için $http.

Tercih Edilen Çözüm: Karma

Testlerinizi çalıştırmak için Karma kullanıyorsanız (ve kullanmanız gerekir), ng-html2js ön işlemcisi ile şablonları sizin için yükleyecek şekilde yapılandırabilirsiniz . Ng-html2js, belirttiğiniz HTML dosyalarını okur ve bunları $templateCache.

1. Adım: Ön işlemciyi etkinleştirin ve yapılandırın. 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'ı uygulamanızı iskele haline getirmek için kullanıyorsanız bu yapılandırma çalışacaktır.

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

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

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

2. Adım: Modülü testlerinizde kullanın

// my-test.js

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

Tam bir örnek için, Angular test gurusu Vojta Jina'nın bu kanonik örneğine bakın . Tam bir kurulum içerir: karma yapılandırması, şablonlar ve testler.

Karma Olmayan Çözüm

Karma'yı herhangi bir nedenle kullanmazsanız (eski uygulamada esnek olmayan bir oluşturma sürecim vardı) ve yalnızca bir tarayıcıda test ediyorsanız $httpBackend, şablonu gerçek olarak almak için ham bir XHR kullanarak ngMock'un devralmasını aşabileceğinizi öğrendim. ve $templateCache. Bu çözüm çok daha az esnektir, ancak şimdilik işi halletmektedir.

// 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);
}));

Cidden, gerçi. Karma kullanın . Kurmak biraz çalışma gerektirir, ancak tüm testlerinizi komut satırından aynı anda birden fazla tarayıcıda çalıştırmanıza izin verir. Böylece bunu sürekli entegrasyon sisteminizin bir parçası olarak alabilir ve / veya düzenleyicinizden bir kısayol tuşu yapabilirsiniz. Alt-sekme-yenileme-reklam-sonsuzdan çok daha iyi.


6
Bu açık olabilir, ancak başkaları aynı şeye takılıp yanıtlar için buraya bakarsa: içindeki bölüme preprocessorsdosya kalıbını (örneğin "path/to/templates/**/*.html") eklemeden de çalışmasını sağlayamazdım . fileskarma.conf.js
Johan

1
Öyleyse devam etmeden önce yanıtı beklememekle ilgili büyük sorunlar var mı? İstek geri geldiğinde değeri güncelleyecek mi (IE 30 saniye sürer)?
Jackie

1
@Jackie falseXHR opençağrısının parametresini eşzamanlı yapmak için kullandığım "Karma olmayan" örnekten bahsettiğinizi varsayıyorum . Bunu yapmazsanız, yürütme neşeyle devam edecek ve şablon yüklenmeden testlerinizi yürütmeye başlayacaktır. Bu da aynı soruna geri dönmenizi sağlar: 1) Şablon talebi gider. 2) Test yürütülmeye başlar. 3) Test bir yönerge derler ve şablon hala yüklenmemiştir. 4) Angular $http, alay edilen hizmeti aracılığıyla şablonu ister . 5) Sahte $httphizmet şikayet ediyor: "beklenmedik istek".
SleepyMurph

1
Karma olmadan homurtu-yasemini çalıştırabildim.
FlavourScape

5
Başka bir şey: karma-ng-html2js-preprocessor ( npm install --save-dev karma-ng-html2js-preprocessor) kurmanız ve stackoverflow.com/a/19077966/859631karma.conf.js adresine göre onu eklentiler bölümüne eklemeniz gerekir .
Vincent

37

Sonunda yaptığım şey, şablon önbelleğini almak ve görünümü oraya koymaktı. NgMock'u kullanmama konusunda kontrole sahip değilim, çıkıyor:

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

26
İşte bu yöntemle ilgili şikayetim ... Şimdi, şablon önbelleğine bir dize olarak enjekte edeceğimiz büyük bir html parçasına sahip olacaksak, ön uçtaki html'yi değiştirdiğimizde ne yapacağız? ? Testteki html de değiştirilsin mi? Sürdürülemez bir cevap olan IMO ve templateUrl yerine şablon seçeneğini kullanmaya gitmemizin nedeni. Yönergede html'mi büyük bir dizge olarak kullanmaktan hiç hoşlanmasam da, html'nin iki yerini güncellemek zorunda kalmamak için en sürdürülebilir çözüm. Bu, html'nin zamanla eşleşemeyeceği kadar fazla görüntüleme gerektirmez.
Sten Muchow

12

Bu ilk sorun, şunu ekleyerek çözülebilir:

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

Bunun nedeni varsayılan olarak ngMock modülünde $ httpBackend'i bulmaya çalışması ve dolu olmamasıdır.


1
Aslında asıl sorunun doğru cevabı buydu (bana yardımcı olan da buydu).
Mat

Bunu denedim, ancak passThrough () hala benim için çalışmadı. Yine de "Beklenmeyen istek" hatası veriyordu.
frodo2975

8

Ulaştığım çözüm için jasmine-jquery.js ve bir proxy sunucusu gerekiyor.

Şu adımları takip ettim:

  1. Karma.conf'ta:

dosyalarınıza yasemin-jquery.js ekleyin

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

fikstürlerinizi sunacak bir proxy sunucusu ekleyin

proxies = {
    '/' : 'http://localhost:3502/'
};
  1. Spesifikasyonunuzda

    define ('MySpec', function () {var $ kapsam, şablon; jasmine.getFixtures (). () {şablon = açısal.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. Uygulamanızın kök dizininde bir sunucu çalıştırın

    python -m SimpleHTTPServer 3502

  3. Karma çalıştırın.

Bunu anlamak biraz zaman aldı, çok sayıda gönderi aramak zorunda kaldım, bence bu konudaki dokümantasyon çok önemli bir konu olduğu için daha net olmalı.


Varlıkları sunarken localhost/base/specsve python -m SimpleHTTPServer 3502çalıştırarak bir proxy sunucusu eklerken sorun yaşıyordum . Siz bir dahisiniz bayım!
pbojinov

Testlerimde $ compile'dan dönen boş bir eleman alıyordum. Diğer yerler $ kapsam çalıştırılmasını önerdi. $ Digest (): hala boş. $ Kapsam çalıştırılıyor. $ Apply () işe yaradı. Sanırım yönergemde bir denetleyici kullandığım için mi? Emin değil. Tavsiye için teşekkürler! Yardım etti!
Sam Simmons

7

Çözümüm:

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'
],

test:

'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');
  }));
});

Geliştiricileri Karma kullanmaya zorlamayan ilk düzgün çözüm. Açısal adamlar bu kadar havalı bir şeyin ortasında neden bu kadar kötü ve kolayca kaçınılabilir bir şey yapsın? pfff
Fabio Milheiro

Bir 'test / mock / ** / *. Js' eklediğinizi görüyorum ve sanırım bu, hizmetler gibi tüm alay konusu şeyleri yüklemek mi? Alay edilen hizmetlerin tekrar kodlanmasını önlemenin yollarını arıyorum. Bize bundan biraz daha fazlasını gösterir misin?
Stephane

tam olarak hatırlamıyorum, ancak muhtemelen $ http hizmeti için JSON'lar gibi ayarlar vardı. Süslü bir şey yok.
bartek

Bugün bu sorunu yaşadım - harika bir çözüm. Karma kullanıyoruz ama aynı zamanda Chutzpah da kullanıyoruz - direktifleri test edebilmek için karmayı kullanmaya zorlanmamız için hiçbir neden yok ve sadece karma.
lwalden

Django'yu Angular ile kullanıyoruz ve bu, templateUrl'yi yükleyen bir yönergeyi test etmek için harika çalıştı static, örneğin, beforeEach(preloadTemplate(static_url +'seed/partials/beChartDropdown.html')); Teşekkürler!
Aleck Landgraf

6

Grunt kullanıyorsanız, grunt-angular-şablonlar kullanabilirsiniz. Şablonlarınızı templateCache'ye yükler ve spesifikasyon yapılandırmanız için şeffaftır.

Örnek yapılandırmam:

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

Aynı sorunu, seçilen çözümden biraz farklı bir şekilde çözdüm.

  1. İlk olarak, karma için ng-html2js eklentisini kurdum ve yapılandırdım . Karma.conf.js dosyasında:

    preprocessors: {
      'path/to/templates/**/*.html': 'ng-html2js'
    },
    ngHtml2JsPreprocessor: {
    // you might need to strip the main directory prefix in the URL request
      stripPrefix: 'path/'
    }
  2. Sonra her şeyden önce oluşturulan modülü yükledim. Spec.js dosyanızda:

    beforeEach(module('myApp', 'to/templates/myTemplate.html'));
  3. Sonra onu bir değişkene depolamak için $ templateCache.get kullandım. Spec.js dosyanızda:

    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. Sonunda bu şekilde test ettim. Spec.js dosyanızda:

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

4

Şablon html'sini dinamik olarak $ templateCache'ye yüklemek için, burada açıklandığı gibi html2js karma ön işlemcisini kullanabilirsiniz.

bu , conf.js dosyasındaki dosyalarınıza ' .html' şablonlarının yanı sıra önişlemciler = {' .html': 'html2js'} eklemekten ibarettir;

ve kullan

beforeEach(module('..'));

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

js test dosyanıza


Ben alıyorumUncaught SyntaxError: Unexpected token <
Melbourne2991

2

Karma kullanıyorsanız , harici HTML şablonlarınızı önceden derlemek için karma-ng-html2js-preprocessor kullanmayı düşünün ve test yürütme sırasında Angular'ın HTTP GET'i denemekten kaçının. Birkaçımız için bununla uğraştım - benim durumumda templateUrl'nin kısmi yolları normal uygulama yürütme sırasında çözüldü, ancak testler sırasında uygulama ile test dizini yapılarındaki farklılıklar nedeniyle çözülmedi.


2

Eğer kullanıyorsanız yasemin-maven-eklentisi RequireJS birlikte kullanabileceğiniz metin eklentisi bir değişken içine şablon içeriği yüklemek ve daha sonra şablon önbelleğinde koydu.


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);
        }));

    });
});

Bunu Karma olmadan yapabilir misin?
Winnemucca

2

Testlerinizde requirejs kullanırsanız, html şablonunu almak ve $ templateCache'ye koymak için 'text' eklentisini kullanabilirsiniz.

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

Tüm şablonları templatecache'de derleyerek bu sorunu çözüyorum. Ben yudum kullanıyorum, homurdanma için de benzer bir çözüm bulabilirsin. Benim şablonum yönergelerimde, modaller şöyle görünür:

`templateUrl: '/templates/directives/sidebar/tree.html'`
  1. Package.json dosyama yeni bir npm paketi ekle

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

  2. Gulp dosyasında templatecache ve yeni bir görev ekleyin:

    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. İndex.html'deki tüm js dosyalarını ekleyin

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

  4. Zevk almak!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.