Контроллер модульного тестирования AngularJS с сервисной зависимостью в Jasmine

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

app.service("StringService", function() {
    this.addExcitement = function (str) {
        return str + "!!!";
    };
});

app.controller("TestStrategyController", ["$scope", "StringService", function ($scope, StringService) {
    $scope.addExcitement = function (str) {
        $scope.excitingString = StringService.addExcitement(str);
    };
}]);

И тест, который я использую в настоящее время:

describe("Test Strategy Controller Suite", function () {
    beforeEach(module("ControllerTest"));

    var $scope, MockStringService;

    beforeEach(inject(function ($rootScope, $controller) {
        $scope = $rootScope.$new();
        MockStringService = jasmine.createSpyObj("StringService", ["addExcitement"]);
        $controller("TestStrategyController", {$scope: $scope, StringService: MockStringService});
    }));

    it("should call the StringService.addExcitement method", function () {
        var boringString = "Sup";
        $scope.addExcitement(boringString);
        expect(MockStringService.addExcitement).toHaveBeenCalled();
    });
});

Этот тест проходит, но я кое-что запутался: если я изменю имя метода в сервисе (скажем, я назову его addExclamations вместо addExcitement, но не там, где он используется в контроллере (все еще говорит $scope.excitingString = StringService.addExcitement(str);), мои тесты по-прежнему проходят, даже несмотря на то, что мой контроллер теперь неисправен. Однако, как только я также изменю имя метода в контроллере, чтобы исправить фактическую поломку, вызванную изменением имени метода службы, мои тесты прерываются, потому что он пытается вызвать старый метод addExcitement.

Это указывало бы на то, что мне нужно будет вручную синхронизировать имена методов со службой, изменив строку объекта jasmine spy на MockStringService = jasmine.createSpyObj("StringService", ["addExclamations"]);.

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

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


person bobbyz    schedule 26.01.2016    source источник


Ответы (1)


Я бы сказал, что это ожидаемый результат того, как работает ваш тестовый код, просто потому, что вы создали «совершенно новый» фиктивный объект службы. Думаю, вы знаете, о чем я говорю.

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

   beforeEach(inject(function ($rootScope, $controller, $injector) {
       $scope = $rootScope.$new();
       MockStringService = $injector.get('StringService'); 
       spyOn(MockStringService , 'addExcitement').andReturn('test');
       $controller("TestStrategyController", {$scope: $scope, StringService: MockStringService});
   }));

обратите внимание, что andReturn() — это метод jasmine 1.x, зависит от версии, которую вы используете, вы можете немного изменить код.

Таким образом, если вы измените имя метода в StringService, вы должны получить ошибки от метода spyOn(), так как метод больше не существует.

Другое дело, что вам не нужно использовать $injector, как это сделал я, чтобы получить экземпляр службы, вместо этого вы можете просто внедрить свою службу. Я не помню, почему я так поступил. :)

person sdfacre    schedule 26.01.2016
comment
Я продолжаю повторять свое мнение о том, чтобы полностью издеваться над сервисом (используя что-то вроде jasmine.createSpyObj()) и приводить ссылку на реальный сервис и делать из него шпиона. Кажется, это еще один аргумент в пользу последнего, потому что он отлично сработал. По-прежнему раздражает необходимость менять тестовый код, если я делаю такое изменение, но я думаю, что это просто характер наличия актуальных/точных тестов. Спасибо! - person bobbyz; 27.01.2016
comment
если вы сделаете подобное изменение, вам все равно придется изменить тестовый код, по крайней мере, для этой строки expect(MockStringService.addExcitement), не так ли? Я думаю, вы можете перебрать все функции фактической службы, шпионить за ними, а затем возвращать ссылку на объект-шпион, чтобы вы могли настроить ожидание позже. Таким образом, вы можете достичь того, чего хотите, но вы можете в конечном итоге протестировать неправильный метод. - person sdfacre; 27.01.2016