How can I get my jasmine tests fixtures to load be

2020-02-27 11:05发布

I am fairly certain that the issue is that the jquery bindings set to run on $(document).ready do not have the fixture html available to them. So when my events occur that are intended to make a change to the DOM via a jquery function, nothing happens, and my tests fail. I saw a "solution" to this problem here, but that solution which did work for me, required changing my working jquery function to bind with the .live method instead of the .click method. I have two issue with this. First I do not want to have to change my working code so that the tests will pass properly. The testing framework ought to test whether the code will work in the app, where the DOM load and the javascript bindings occur in the correct order. The second issue I have with the solution is that .on and .delegate both did not work for some reason and the only thing that ended up working was to use the .live method which is currently in the process of being deprecated. In conclusion, I would like to figure out how to change either my tests or the testing framework itself, such that the fixtures are loaded before the functions that run in $(document).ready.

I am working with rails 3.2.8 and I have two different branches set up to experiment with jasmine testing. One of the branches uses the jasminerice gem and the other uses the jasmine-rails gem. The Javascript and jQuery(when using the .live method) tests all pass properly in both of these set-ups.

Here is a description of the branch using jasminerice:

Here are the lines from my Gemfile.lock file describing the jasmine and jQuery set-up:

jasminerice (0.0.9)
  coffee-rails
  haml
jquery-rails (2.1.3)
  railties (>= 3.1.0, < 5.0)
  thor (~> 0.14)

Jasminerice includes the jasmine-jquery extension by default. The jasmine-jQuery extension provides the toHaveText method I am using in the tests.

The test file jasmine_jquery_test.js which is located directly within the spec/javascripts directory contains this content:

#= require application

describe ("my basic jasmine jquery test", function(){

    beforeEach(function(){
        $('<a id="test_link" href="somewhere.html">My test link</a>').appendTo('body');
    });

    afterEach(function(){
        $('a#test_link').remove();
    });

    it ("does some basic jQuery thing", function () {
        $('a#test_link').click();
        expect($("a#test_link")).toHaveText('My test link is now longer');
    });

    it ("does some the same basic jQuery thing with a different trigger type", function () {
        $('a#test_link').trigger('click');
        expect($("a#test_link")).toHaveText('My test link is now longer');
    });

});
describe ('subtraction', function(){

    var a = 1;
    var b = 2;

    it("returns the correct answer", function(){
        expect(subtraction(a,b)).toBe(-1);
    });

});

My javascript file tests.js which is located in the app/assets/javascripts dir has this content:

function subtraction(a,b){
    return a - b;
}

jQuery (function($) {
/* 
    The function I would rather use - 
    $("a#test_link").click(changeTheTextOfTheLink)
    function changeTheTextOfTheLink(e) {
        e.preventDefault()
        $("a#test_link").append(' is now longer');
    }
*/

    $("a#test_link").live('click', function(e) {
        e.preventDefault();
        $("a#test_link").append(' is now longer');
    });

});

My application.js file located in the same app/assets/javascripts dir has this content:

//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require vendor
//= require_tree . 

And the description of the jasmine-rails branch can be found in the content of my first jasmine test related stackoverflow question.

So if anyone has an idea how to manipulate the tests such that the $(document).ready functions are run after the fixtures are loaded I would very much like to hear your thoughts?

6条回答
迷人小祖宗
2楼-- · 2020-02-27 11:34

Adding the jQuery directly to the html fixture worked for me to get the desired behavior. It is not exactly an answer to this question, but I am starting to believe it's the best solution currently available. My changes where made in the jasmine-rails gem set-up and I haven't yet tried it in the jasminerice branch.

Here is my test file:

describe ("my basic jasmine jquery test", function(){

    beforeEach(function(){
        loadFixtures('myfixture.html');
    });

    it ("does some basic jQuery thing", function () {
        $('a#test_link').click();
        expect($("a#test_link")).toHaveText('My test link is now longer');
    });

    it ("does the same basic jQuery thing with a different event initiation method", function () {
        $('a#test_link').trigger('click');
        expect($("a#test_link")).toHaveText('My test link is now longer');
    });

});
describe ('substraction', function(){

    var a = 1;
    var b = 2;

    it("returns the correct answer", function(){
        expect(substraction(a,b)).toBe(-1);
    });

});

And here is my fixture file:

<a id="test_link" href="somewhere.html">My test link</a>

<script>
    $("a#test_link").click(changeTheTextOfTheLink)
    function changeTheTextOfTheLink(e) {
        e.preventDefault()
        $("a#test_link").append(' is now longer');
    }
</script>
查看更多
叼着烟拽天下
3楼-- · 2020-02-27 11:35

Have you tried jasmine-jquery?

https://github.com/velesin/jasmine-jquery

From it's github page,

jasmine-jquery provides two extensions for Jasmine JavaScript Testing Framework:

a set of custom matchers for jQuery framework an API for handling HTML, CSS, and JSON fixtures in your specs

查看更多
放我归山
4楼-- · 2020-02-27 11:38

Personally, I consider this to be a bug jasmine-jquery. I "solved" this by making my fixtures part of the SpecRunner.html. Not ideal but it works in simple cases.

查看更多
男人必须洒脱
5楼-- · 2020-02-27 11:38

I took a slightly different approach to those described here to solve this limitation.

Disclaimer: I work for Microsoft so I cannot post my actual source code here without legal approval, so instead I'll just describe an approach that works well.

Rather than trying to change how document.ready works, I modified the jasmine-jquery source to support a function called fixturesReady that takes a callback.

Essentially this is defined inside an IIFE, and holds an array of callbacks per event name privately, that are iterated over on trigger.

Then on this chunk you can put the trigger:

jasmine.Fixtures.prototype.load = function() {
  this.cleanUp()
  this.createContainer_(this.read.apply(this, arguments))

  fixturesReady.trigger("loaded", arguments); // <----- ADD THIS

}

So in your tests now you can code against:

fixturesReady(yourCallback)

And you know that when yourCallback is invoked you can safely query the DOM for your fixture elements.

Additionally, if you're using the async.beforeEach jasmine plugin you could do this:

async.beforeEach(function(done){

    fixturesReady(done);

    ...

    loadFixtures("....");

});

Hope that helps, and serves to give you some ideas about solving this problem.

NOTE: Remember in your plugin to clear any callbacks in an afterEach block if necessary :)

查看更多
我只想做你的唯一
6楼-- · 2020-02-27 11:58

It has been a while since this question was asked (and an answer accepted).

Here is a better approach to structure your test and load the script for testing with jasmine.

templates/link.tmpl.html

<a id="test_link" href="somewhere.html">My test link</a>

js/test-link.js

$("a#test_link").click(changeTheTextOfTheLink)
function changeTheTextOfTheLink(e) {
    e.preventDefault()
    $("a#test_link").append(' is now longer');
}

specs/link-test.spec.js

describe ("my basic jasmine jquery test", function(){

    jasmine.getFixtures().fixturesPath = ''

    var loadScript = function(path) {
      appendSetFixtures('<script src="' + path + '"></script>');
    };

    beforeEach(function(){
        loadFixtures('templates/link.tmpl.html');
        loadScript('js/test-link.js');
    });

    it ("does some basic jQuery thing", function () {
        $('a#test_link').click();
        expect($("a#test_link")).toHaveText('My test link is now longer');
    });

    it ("does the same basic jQuery thing with a different event initiation method", function () {
        $('a#test_link').trigger('click');
        expect($("a#test_link")).toHaveText('My test link is now longer');
    });

});

Spec Runner

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>Spec Runner</title>

  <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.3/jasmine.css">
  <script type="text/javascript" src="lib/jasmine-2.0.3/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.3/jasmine-html.js"></script>
  <script src="lib/jasmine-2.2/boot.js"></script>

  <script type="text/javascript" src="lib/jquery-2.1.3.min.js"></script>
  <script type="text/javascript" src="lib/jasmine-jquery.js"></script>

  <script type="text/javascript" src="specs/link-test.spec.js"></script>
</head>

<body>
</body>
</html>
查看更多
兄弟一词,经得起流年.
7楼-- · 2020-02-27 12:01

I used dom_munger to automatically generate the fixture for me, so adding the jquery code manually inside the fixture isn't an option for me. I've been searching for the same answer, and the best solution I can think of right now is to use event delegate in jQuery.

For example, instead of writing this:

$('#target').click(function(){
    // Do work
});

Do this:

$(document).on('click', '#target', function(){
    // Do work
});

In this way, the listener is attached to the document, which is present before the fixture is loaded. So when the click event happens on #target, it bubbles up to document. Document checks if the origin matches the second argument - '#target'. If it matches, the handler function will be called.

I know there are limitations to this, such as initializing plug-ins, but it does solve part of the problem.

查看更多
登录 后发表回答