I'm looking to write some tests that test the wiring between Backbone views and their models. I essentially want to load up my view with a model and make sure everything is peachy (events properly triggered/handled, elements added to the DOM, etc). I see this as different from acceptance/functional/e2e testing, but larger than simple unit tests. In other words, I'm not looking to write:
var browser = new Browser()
, fakeData = readFixtures("persons.json");
fakeAPIResponse('/persons', fakeData);
browser.visit("http://localhost:3000/", function () {
assert.ok(browser.success);
var persons = browser.queryAll(".persons li");
assert.lengthOf(persons, 20);
});
but rather something like
var router = require('routers/main'),
UserModel = require('models/user'),
AccountView = require('views/account');
...
# In a test
var model = new UserModel({ userId: 1 });
router._showView(new AccountView({ model: model });
expect(document.getElementsByClassName('account-panel')).to.have.length(1);
model.set('name', 'Test Testerson');
expect(document.getElementById('name-field').value).to.equal('Test Testerson');
Maybe I'm way off and should just put together some end-to-end tests but this seems to me like it'd be a valuable way of testing. My question is: how can I accomplish this? I need a full DOM, so I'm thinking that these should run in something like PhantomJS; the DOM should be reset before each test, but it seems silly/inefficient to have the browser navigate to a new page for each individual test. Is there a framework out there for running a test this way? Feel free to tell me I'm wrong for wanting this.
We are doing this using casperJs. Casper will give you the full DOM, but this should not be the reason the choose integration test over unit test. When I find myself going way out of my way to create a test environment, I tend to choose integration.
However, having said that, I think you can get away with doing a unit test and here is how I would do it. We are using testem with chaiJs assertions for such tests. If you setup a fixture, you can do something like this (and you'll have the full DOM, and ability to test events and everything else listed in your OP).
beforeEach(function () {
var theModel = new Backbone.Model(),
theViewToTest = new TheViewToTest({
model: theModel,
el: $(fixtures.get('some-fixture.html'))
});
theViewToTest.render();
this.theViewToTest = theViewToTest;
});
describe('Checking event handlers', function () {
it('Should fire some events', function () {
// I would setup a spy here and listen to the button handler
this.theViewToTest.$el.find('.some-button').trigger('click');
// Assert the spy was called once
});
});
Related
I'm running into issue with Protractor when accessing variable that stores return value of "elements.all". I'm fairly new to Protractor, so I wasn't sure how to select elements by custom attribute. Luckily, I received a suggestion, when I posted a question in another post. I was suggested to try out - "element.all(by.css('[mycustom-id]'));". But I'm not sure if that statement works or not since I'm getting "Could not find testability for element" error. It is also possible that I'm incorrectly iterating the object. I appreciate if anyone of you can point out my mistake. Thanks.
Spec.JS
var settings = require(__dirname + '/setting.json');
describe('Protractor Demo App', function() {
var target = element.all(by.css('[mycustom-id]'));
beforeEach(function() {
browser.get(settings.url);
});
it('Test mouseover', function() {
// This does not work
target.each(function(item){
//Do some stuff here
});
// This does not work either
target.count().then(function(x){
console.log("Total--" + x);
});
});
});
index.html
<div>
<a mycustom-id="123" href=''>HELLO1</a>
<a mycustom-id="211" href=''>HELLO2</a>
</div>
I'm getting this error because I need to set useAllAngular2AppRoots to true in config file. So if anyone having similar issue, make sure you have useAllAngular2AppRoots set to True.
It's not a good practice to put things outside of Jasmine functions, i.e. outside of it(), beforeAll() etc. Protractor uses those Jasmine functions to manage the control flow.
So I'm guessing that it is trying to create those webElements way before it should be. Move your element locator inside the it() block.
it('Test mouseover', function() {
var target = element.all(by.css('[mycustom-id]'));
target.each(function(item){
//Do some stuff here
});
});
My first run at E2E tests. I'm trying to digest someone else's protractor tests.
Problem: There are a lot of browser.driver.sleep and this seems fragile.
Goal: not to use browser.driver.sleep
Question: What is a better approach to browser.driver.sleep? Something less fragile like a promise or something I dont know about lol?
var config = require('../../protractor.conf.js').config;
describe('this Homepage Body Tests', function(){
browser.driver.get(config.homepageUrl);
it("should open find a clinic page", function(){
// page loads :: want to fix this random wait interval
browser.driver.sleep(2000);
browser.ignoreSynchronization = true;
var string = 'clinic';
var main = '.search-large-text';
var link = element(by.cssContainingText('.submenu li a', string));
link.click().then(function() {
// page reloads :: want to fix this random wait interval
browser.driver.sleep(3000);
var title = element(by.cssContainingText(main, string));
expect(title.getText()).toBe(string);
});
});
});
Since there is an ignoreSynchronization turned on, you cannot use waitForAngular(), which would be a solution in case of an angular-site testing.
A better solution here would be to set a page load timeout:
browser.manage().timeouts().pageLoadTimeout(10000); // 10 seconds
See also these relevant threads on explicit waits and timeouts:
Use protractor to test login on non-AngularJS page (Leo's answer is very detailed)
Protractor : How to wait for page complete after click a button?
Timeouts
I have a Backbone app written in CoffeeScript. I'm trying to use Mocha (with Chai and Sinon) to write tests for DOM-related behaviors, but it seems that hidden fixtures (I'm using js-fixtures now but I've also tried this unsuccessfully with a hidden '#fixtures' div) don't register certain DOM-related behaviors which makes testing certain types of DOM-related behaviors (seemingly) impossible.
For example, my main app view has several subviews which are never rendered at the same time: when the app view renders subview A, it remembers the focused element of the currently active subview B (#_visibleView), saves that information on subview B, closes the subview B, and then renders subview A.
_rememberFocusedElement: ->
focusedElement = $ document.activeElement
if focusedElement
focusedElementId = focusedElement.attr 'id'
if focusedElementId
#_visibleView?.focusedElementId = focusedElementId
This works when I test it manually, but when I try to write unit tests for this behavior they fail because I can't set focus (e.g., via $(selector).focus()) to an element in a hidden div/iframe. (I have the same issue with functionality which listens for window resize events.)
I thought that if I changed $ document.activeElement to #$ ':focus" I might get different results, but that doesn't fix the issue.
Here is what the relevant parts of my Mocha (BDD) tests look like. This spec will print TEXTAREA to the console and then undefined, indicating that there is a textarea with id='transcription' but I can't set focus to it.
beforeEach (done) ->
fixtures.path = 'fixtures'
callback = =>
#$fixture = fixtures.window().$ "<div id='js-fixtures-fixture'></div>"
#appView = new AppView el: #$fixture
done()
describe 'GUI stuff', ->
it 'remembers the currently focused element of a subview', (done) ->
#appView.mainMenuView.once 'request:formAdd', =>
#appView._visibleView.$('#transcription').focus()
console.log #appView._visibleView.$('#transcription').prop 'tagName'
console.log #appView._visibleView.$(':focus').prop 'tagName'
done()
#appView.mainMenuView.trigger 'request:formAdd'
Is there any way that I can write unit tests for these types of behaviors?
Ok, first off let me clarify something: the term "unit test" means man different things to many people. Often times it becomes synonymous with "any test written using a unit test framework (like Mocha)". When I use the term "unit test" that's not what I mean: what I mean is a test that tests only a single unit of work (which, in a JS environment, will usually be a single function, but might be a whole class).
Ok, with that out of the way, if you really are trying to unit test your code, you're sort of taking the wrong approach. A unit test really shouldn't rely on anything outside the context of the function being tested, and so relying on the (external) DOM is where your problem lies.
Let's assume your focus-handling code is in a function called handleFocus (I don't know the actual method name). Consider the following test, which I'll write using JavaScript since my CoffeScript is rusty:
describe('#handleFocus', function() {
it('remembers the currently focused element of a subview', function() {
var setFocusStub = sinon.stub($.fn, 'focus');
appView._visibleView.handleFocus();
expect(setFocusStub.calledOnce).to.be(true);
});
});
The above is a bit of an over-simplification, but hopefully it illustrates the point. What you're really trying to check isn't whether the DOM (fake or real) does X; what you're trying check is whether your function does X. By focusing on that in your test, and relying on a stub that checks whether "X" happened or not, you completely eliminate the need for the DOM to be involved.
Now of course you might wonder: "well great, that helps me in test-land, but how do I know it will work in a real environment?" My answer to that would be that your (probably Selenium-based) acceptance tests should cover that sort of thing. Acceptance tests should check whether your overall code works in the real world, while unit tests should ensure that individual pieces of that code work in a fake environment.
The former is great for ensuring your customers don't see bugs, while the latter is great for figuring out exactly where those bugs come from, and for making it possible for you to refactor safely.
I have to say I'm kinda stuck with using jasmine(-node) in (most probably) a slightly wrong way. Currently I have jasmine tests so, that they need to be run in a correct order. I would at the moment need to have a file that can collect and initiate the different jasmine-files in the correct order.
Now I appreciate advice, which show me other ways of making the testing work well and not necessarily just fix this immediate problem, my testing skills are fairly limited, mostly because as a long time solo-coder I haven't had that much use for them.
So the actual problem is that I have test-specs:
mapGenerator-spec.js // Initializes / generates a clean map.
orders-spec.js // Simulates orders given by players (turn based)
map-spec.js // tests the part which is used front-end and backend, creating map from components
moveOrders-spec.js // Generates a part of the turn by resolving orders -> moving units.
So simply the logic in a game is that first generates a map, order generate orders given by players
These need to be executed in the precise order, because they create database-entries that are dependant on the previous test. I would like to keep the tests as product-ready / real as possible and not try to skip the database-inserts / fetches. There is a separate test-database, where the tests are generated.
So can you advice me, what you think is the correct way to do this (if this is not a good way) and / or advice me, if needed, how can I sum up these tests to one collection test "testAll-spec.js", which would run the tests synchronously.
If you need something to happen before you run a test, then you should use beforeEach().
You can also nest jasmine tests, and run a beforeEach() within each test, as below:
describe('testInitialiser', () => {
var firstVariable;
beforeEach(() => {
firstVariable = 1;
});
it('should set firstVariable to 1', () => {
expect(firstVariable).toBe(1);
});
// this is a nested test - these tests will run after the above beforeEach.
describe('nested test ', () => {
// all tests within this describe will have access to firstVariable.
var secondVariable;
beforeEach(() => {
secondVariable = firstVariable + 1;
});
it('should set secondVariable to firstVariable + 1 ', () => {
expect(secondVariable).toBe(2);
});
});
});
Hope this helps.
Let's say you want to be able to create a Backbone.Router instance that calls Backbone.history.start, destroy all traces of it, and create it again.
I need to do this for unit testing.
The problem is that creating a Backbone Router has global implications. Like Backbone.history, which is undefined until you make it.
var ReversibleRouter = Backbone.Router.extend({
initialize: function(){
_buildAppPaths(this.options) // Implementation not shown. Builds the application paths
Backbone.history.start()
},
destroy: function(){
// TODO: Implement this
}
})
I'd like to be able to create and completely destroy so that I can do some unit testing on my application's implementation of the Router.
It it enough to call Backbone.history.stop() and set Backbone.history = undefined?
If you're looking to do more full stack integration types of testing, you can reset your application's router by using:
Backbone.history.stop()
Backbone.history = undefined // Could also probably use delete here :)
This has been successful so far--our tests probably instantiate and kill the router 5 times during our qunit testing. Works great.
Maybe mocking like #AdamTerlson is saying is the better way to go long term, but I'm still learning unit and integration testing philosophy. I don't mind having a full stack test or two, though.
This might be cheating, but I'm of the opinion you should be mocking functions outside of your unit of functionality that your testing and be especially sure your unit tests are not be allowed to hit the live DOM, server or the browser.
Look into SinonJS, it's great for this.
Here's an example of testing the initialize method of the router without ever affecting the browser history state.
test('...', function () {
// Arrange
var buildAppPaths = sinon.stub(ReversibleRouter.prototype, '_buildAppPaths');
var startHistory = sinon.stub(Backbone.history, 'start');
var options = {};
// Act
new ReversibleRouter(options);
// Assert
ok(buildAppPaths.calledWith(options));
ok(startHistory.calledOnce); // or ok(Backbone.history.start.calledOnce);
});
Under this approach, you have successfully asserted that all calls were made to external units of functionality, including params. At that point, you can trust that the external call will do its job (proven by additional unit tests).
To make calls outside of this one unit would be doing integration tests and not unit tests.