การทดสอบ $scope ในคอนโทรลเลอร์ AngularJs โดยขึ้นอยู่กับ $filter

ฉันได้อ่านบทช่วยสอนและตัวอย่างพื้นฐานมาบ้างแล้ว แต่ฉันประสบปัญหาในการเขียนการทดสอบหน่วยสำหรับคอนโทรลเลอร์ของฉัน ฉันเคยเห็นข้อมูลโค้ดที่สร้างอินสแตนซ์ให้กับคอนโทรลเลอร์และปล่อยให้เชิงมุมฉีดวัตถุ $rootScope ซึ่งจะถูกใช้เพื่อสร้างวัตถุ scope ใหม่สำหรับคอนโทรลเลอร์ แต่ฉันไม่เข้าใจว่าทำไม ctrl.$scope? ไม่ได้กำหนดไว้:

 describe('EmployeeCtrl', function () {
    var scope, ctrl, $httpBackend;

    beforeEach(inject(function (_$httpBackend_, $rootScope, $controller, $filter) {
        $httpBackend = _$httpBackend_;       

        scope = $rootScope.$new();
        ctrl = $controller('EmployeeCtrl', { $scope: scope});
        expect(ctrl).not.toBeUndefined();
        expect(scope).not.toBeUndefined();   //<-- PASS!      
        expect(ctrl.$scope).not.toBeUndefined();  //<-- FAIL!       
    }));
});

ฉันลงเอยด้วยการใช้ตัวแปร scope แทน ctrl.$scope แต่ในการทดสอบครั้งแรก ฉันไม่สามารถหาวิธีทดสอบ ตัวแปรฟังก์ชัน ภายในคอนโทรลเลอร์ของฉันได้:

ตัวควบคุม:

 function EmployeeCtrl($scope, $http, $filter, Employee) {
  var searchMatch = function (haystack, needle) {
   return false;
  }
 }

การทดสอบหน่วยที่เสียหาย:

it('should search ', function () {                
    expect(ctrl.searchMatch('numbers','one')).toBe(false);
});

นี่คือสิ่งที่ฉันได้รับ

TypeError: Object # ไม่มีวิธี 'searchMatch'

คุณจะทดสอบฟังก์ชั่นนั้นได้อย่างไร? เพื่อเป็นวิธีแก้ปัญหา ฉันย้ายวิธีการของฉันไปที่ $scope เพื่อทดสอบหา scope.searchMatch แต่ฉันสงสัยว่านี่เป็นวิธีเดียวหรือไม่

สุดท้าย ในการทดสอบของฉันปรากฏว่า $filter ไม่ได้กำหนดเหมือนกัน คุณจะฉีดมันได้อย่างไร ฉันลองสิ่งนี้แต่ไม่ได้ผล:

ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter });

ขอบคุณ

อัปเดต: วิธีการดังกล่าวข้างต้นเพื่อแทรก $filter ก็ใช้ได้ดี


person Ulises    schedule 07.01.2013    source แหล่งที่มา
comment
การทดสอบดอกมะลิของฉันแตกในวิธีการฉีด มันไม่พบ. ฉันขาดอะไรไป. เชิงมุมไม่เป็นโมฆะ ฉันมีการทดสอบที่ผ่านการทดสอบเพื่อยืนยันว่าโหลดได้   -  person Hcabnettek    schedule 22.03.2013


คำตอบ (4)


ความเข้าใจของฉันคือใน Angularjs คุณส่งวัตถุขอบเขตไปยังคอนโทรลเลอร์เมื่อแอปพลิเคชันเริ่มทำงานและตัวควบคุมแก้ไขขอบเขต คอนโทรลเลอร์จะไม่ถูกเรียกอีกต่อไป

สิ่งที่คอนโทรลเลอร์กำลังทำจริงๆ ใน ​​Angularjs กำลังเริ่มต้นขอบเขต: คอนโทรลเลอร์จะทำงานเพียงครั้งเดียวเท่านั้น หากคุณเข้าใจสิ่งนี้ คุณจะรู้ว่าการขอขอบเขตเช่นนี้จากผู้ควบคุม:

currentScope = myController.scope;

ไม่สมเหตุสมผลเลย

(บังเอิญว่าสิ่งหนึ่งที่ฉันไม่ชอบใน Angular ก็คือชื่อที่พวกเขาเลือกไว้ ถ้าสิ่งที่ 'Controller' ทำคือการเริ่มต้นขอบเขต มันก็ไม่ใช่ Controller จริงๆ นะ มีสิ่งนี้มากมายใน Angular เอพีไอ)

ฉันคิดว่าวิธีที่ 'เหมาะสม' ในการทดสอบ 'ตัวควบคุม' คือการสร้างขอบเขตว่างใหม่ตั้งแต่ต้นในส่วนคำสั่ง Jasmine beforeEach และใช้ 'ตัวควบคุม' สำหรับ การเริ่มต้น ขอบเขตว่างใหม่เช่นนี้::

var ctrl, myScope;

beforeEach(inject(function($controller, $rootScope) {
    myScope = $rootScope.$new();
    ctrl = $controller('myController', {
        $scope: myScope
    });
}));

จากนั้นการทดสอบขอบเขตที่สร้างขึ้นใหม่จะมีคุณสมบัติที่คาดหวัง:

it('In the scope, the initial value for a=2', function() {
            expect(myScope.a).toBe(2);
});

กล่าวอีกนัยหนึ่ง คุณไม่ได้ทดสอบคอนโทรลเลอร์ คุณทดสอบขอบเขตที่คอนโทรลเลอร์สร้างขึ้น

ดังนั้นคุณทำถูกต้องแล้ว

person Robert    schedule 11.01.2013
comment
นั่นสมเหตุสมผลมากขึ้น ขอบคุณมาก ! - person Ulises; 13.01.2013

นี่เป็นอีกรูปแบบหนึ่งที่อาจปฏิบัติตามได้ง่ายกว่า:

var $scope;
beforeEach(module(function ($provide) {
  $scope = {
    // set anything you want here or nothing
  };
  $provide.value('$scope', $scope);
}));

var ctrl;
beforeEach(inject(function ($controller) {
  ctrl = $controller('MyController');
}));

it('should test something...', function () {
  // $scope will have any properties set by
  // the controller so you can now do asserts
  // on them.
});

คุณสามารถใช้รูปแบบเดียวกันนี้เพื่อตั้งค่าสำหรับ $location และ $window รวมถึงค่าอื่นๆ

person Guy    schedule 04.10.2014

มีเพียงสองความเป็นไปได้เท่านั้น:

เพิ่ม searchMatch ในขอบเขต (ตามที่คุณพูดถึง) หรือ

กลับฟังก์ชัน searchMatch:

function EmployeeCtrl($scope, $http, $filter, Employee) {
  return {
    searchMatch: function (haystack, needle) {
      return false;
    }
  };
 }

ถ้ามันต้องเป็นส่วนตัวจริงๆ คุณจะต้องทดสอบฟีเจอร์ที่ใช้งานมัน

หากต้องการรับ $filter คุณสามารถลองทำสิ่งต่อไปนี้:

var $filter = angular.injector(['ng']).get('$filter'); 
person asgoth    schedule 07.01.2013
comment
ขอบคุณ และฉันจะฉีด $filter ได้อย่างไร - person Ulises; 08.01.2013
comment
ฉันได้อัปเดตคำตอบของฉันแล้ว ลองใช้ angular.injector() ซึ่งส่งคืนวัตถุ $injector ซึ่งมีบริการเชิงมุมเช่น $filter, $controller, ... - person asgoth; 08.01.2013

หากต้องการทดสอบตัวกรอง ให้ทำดังต่อไปนี้เพื่อรับตัวกรองเป้าหมาย:

var ctrl, myScope, $filter;
beforeEach(inject(function(_$filter_){
    $filter = _$filter_;
}))

หากชื่อตัวกรองของคุณคือ AccountTimeZone ให้ทำดังนี้

let accountTimeZoneFilter = $filter('AccountTimeZone');
person MING WU    schedule 24.05.2021