10

Одностраничное приложение, над которым я работаю, имеет вид входа в систему с двумя формами: формой входа и формой регистрации. Следующая спецификация описывает тесты для этих форм. Я использую Jasmine-jQuery 1.4.2.Тест Jasmine с использованием .toHaveBeenCalledWith не подходит для регистрации формы

// user_spec.js 

describe("User", function() { 

    var userController; 

    beforeEach(function() { 
    loadFixtures('menu.html'); 
    userController = new MyApp.User.Controller(); 
    }); 

    describe("LoginView", function() { 

    beforeEach(function() { 
     // Mock the $.ajax function to prevent XHR: 
     spyOn($, "ajax").andCallFake(function(params) {}); 
    }); 

    it("should pass email and password with the 'signInForm:submit' event.", function() { 
     var email = "[email protected]"; 
     var password = "Passw0rd"; 
     var callback = jasmine.createSpy("FormSubmitSpy"); 

     userController.loginView.$el.find("#signInEmail").val(email); 
     userController.loginView.$el.find("#signInPassword").val(password); 
     userController.loginView.bind("signInForm:submit", callback, this); 
     userController.loginView.ui.signInForm.trigger("submit"); 

     expect(callback).toHaveBeenCalledWith({ 
     email: email, 
     password: password 
     }); 
    }); 

    it("should pass name, email and password with the 'signUpForm:submit' event.", function() { 
     var name = "John Doe"; 
     var email = "[email protected]"; 
     var password = "Passw0rd"; 
     var callback = jasmine.createSpy("FormSubmitSpy"); 

     userController.loginView.$el.find("#signUpName").val(name); 
     userController.loginView.$el.find("#signUpMail").val(email); 
     userController.loginView.$el.find("#signUpPassword").val(password); 
     userController.loginView.$el.find("#signUpPasswordConfirmation").val(password); 
     userController.loginView.bind("signUpForm:submit", callback, this); 

     userController.loginView.ui.signUpForm.trigger("submit"); 

     expect(callback).toHaveBeenCalledWith({ 
     name: name, 
     email: email, 
     password: password, 
     password_confirmation: password 
     }); 
    }); 

    }); 

}); 

Тест для входа в форме проходит успешно, однако тест на форме регистрации не удается.

Error: Expected spy FormSubmitSpy to have been called with \ 
    [ { name : 'John Doe', email : '[email protected]', \ 
    password : 'Passw0rd', password_confirmation : 'Passw0rd' } ] \ 
    but it was never called. 

    at new jasmine.ExpectationResult (http://localhost:3000/assets/jasmine.js?body=1:114:32) 
    at null.toHaveBeenCalledWith (http://localhost:3000/assets/jasmine.js?body=1:1235:29) 
    at null.<anonymous> (http://localhost:3000/assets/user_spec.js?body=1:233:24) 
    at jasmine.Block.execute (http://localhost:3000/assets/jasmine.js?body=1:1064:17) 
    at jasmine.Queue.next_ (http://localhost:3000/assets/jasmine.js?body=1:2096:31) 
    at jasmine.Queue.start (http://localhost:3000/assets/jasmine.js?body=1:2049:8) 
    at jasmine.Spec.execute (http://localhost:3000/assets/jasmine.js?body=1:2376:14) 
    at jasmine.Queue.next_ (http://localhost:3000/assets/jasmine.js?body=1:2096:31) 
    at jasmine.Queue.start (http://localhost:3000/assets/jasmine.js?body=1:2049:8) 
    at jasmine.Suite.execute (http://localhost:3000/assets/jasmine.js?body=1:2521:14) 

Использование форм в приложении не представляет проблемы. Данные передаются. Все работает нормально. Просто тест не делает.

Обход

Испытание однако успешно, когда я задержать его исполнение.

_.defer(function() { 
    expect(callback).toHaveBeenCalledWith({ 
    name: name, 
    email: email, 
    password: password, 
    password_confirmation: password 
    }); 
}); 

Почему эта работа и «нормальная» реализация не выполняются?


Вот упрощение данного случая:

it("should evaluate true", function() { 
    var foo = false; 
    _.defer(function() { 
    foo = true; 
    }); 
    expect(foo).toBeTruthy(); 
}); 
+0

Что произойдет, если вы удалите тест signInForm? Или переместить его после теста signUpForm? –

+0

Возможно, что-то другое останавливает событие, а затем перезапускает после некоторого действия. Похоже, что в представлении формы не нужно обновлять страницу. Если стоппер не запускается синхронно, тогда этот шпион будет терпеть неудачу. – fncomp

ответ

1

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

Я нашел эту страницу: http://trevmex.com/post/7017702464/nesting-spies-to-see-if-a-callback-in-a-deep-nest-has полезен, так как описывает вашу проблему и потенциальную работу.

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

2

Жасмин способ сделать то же самое, не используя функцию подчеркивания для отсрочки будет следующим:

var flag = false; 
... 

runs(function() { 
    userController.loginView.ui.signInForm.trigger("submit"); 
    setTimeout(function() { flag = true; }, 1); 
} 

waitsFor(function() { 
    return flag; 
}, "Blah this should never happen", 10); 

runs(function() { 
    expect(callback).toHaveBeenCalledWith({ 
    name: name, 
    email: email, 
    password: password, 
    password_confirmation: password 
    }); 
} 

@Marc правильно, что вопрос с помощью привязки и способ Javascript отправляет события «иногда/обычно/всегда "в следующий цикл событий (как работает его асинхронный характер), так как вы следите за обратным вызовом, вы хотите убедиться, что ваши тесты написаны для учета этого асинхронного поведения.

Поскольку ваши тесты написаны, вы рискуете, что первый тест не пройдет ни спорадически (я удивлен, что он работает так, как есть). Вы тестируете асинхронный обратный вызов события в неасинхронном режиме ... имеет смысл?