Testing polymer 1.0 components with iron-ajax usin

2019-07-17 20:07发布

问题:

I am having the hardest time mocking the server response to an iron-ajax component inside my custom component. Here is my code files.

custom-component.html:

<link rel="import" href="/iron-ajax/iron-ajax.html">
<link rel="import" href="/internal-component/internal-component.html">

<dom-module id="custom-component">
    <template>
        <iron-ajax url="staticFile.json" auto handle-as="json" last-response={{ajaxResponse}}></iron-ajax>
        <template is="dom-repeat"
                  items={{ajaxResponse}}
                  sort="_sort"
                  id="gridRow">
            <internal-component var1={{item.var1}}
                                   var2={{item.var2}}>
            </internal-component>
        </template>
    </template>
</dom-module>
<script>(some cool scripts that are working...)</script>

custom-component-tests.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <script src="/webcomponentsjs/webcomponents-lite.js"></script>
    <script src="/web-component-tester/browser.js"></script>
    <script src="/test-fixture/test-fixture-mocha.js"></script>

    <link rel="import" href="/test-fixture/test-fixture.html" />

    <link rel="import" href="/polymer/polymer.html">
    <link rel="import" href="/polymer-ts/polymer-ts.html">

    <link rel="import" href="custom-component.html">
</head>
<body>
<test-fixture id="testElement">
    <template>
        <custom-component></custom-component.>
    </template>
</test-fixture>

<script>
    suite('<custom-component>', function () {
        var testElement;
        var server;
        var responseHeaders = {
            json: { 'Content-Type': 'application/json' },
            plain: { 'Content-Type': 'text/plain' }
        };
        setup(function () {
            replace('custom-component').with('fake-custom-component');
            server = sinon.fakeServer.create();
            server.respondWith('GET', /staticFile\.json/, [
                200,
                responseHeaders.json,
                '[{"var1": "9a","var2": "17n"}]'
            ]);
            testElement = fixture("testElement");
        });
        teardown(function () {
            server.restore();
        });
        suite('testSuite', function () {
            test('test1', function () {
                var ajax = testElement.getElementsByTagName('iron-ajax')[0];
                ajax.lastResponse = null;
                ajax.generateRequest();
                server.respond();
                assert(ajax.lastResponse.hour === "9a");
            });
        });
    });
</script>

</body>
</html>

You'll notice that I'm explicitly calling the iron-ajax generateRequest because if I didn't, then the request wouldn't even happen until after my test completed (and failed). When calling generateRequest explicitly, I am at least able to make the request happen, but (even though I am calling server.respond()) iron-ajax doesn't call _handleResponse until after the test completes. And, even when it does, it is not setting lastResponse because there is a line of code in iron-ajax that checks if (request === this.lastRequest) (which it isn't).

What am I doing wrong?

回答1:

I have found a better solution to test the auto functionality of iron-ajax elements in my elements.

You need to add an event listener on the request of your ajax to fire your server response, no need for generateRequest and no need for setTimeout hacks.

here is an example:

            test('test with ajax element', function (done) {
                var ajax = Polymer.dom(myElement.root).querySelector("#ajax_element");

                ajax.addEventListener('request', function (e) {
                    server.respond('GET', '/CALLED_URL', dataResponse);
                });

                ajax.addEventListener('response', function (e) {
                    //DO YOUR EXPECTS HERE
                    done();
                });
            });


回答2:

I guess a good night's sleep is what I needed.

I forgot to account for asynchronous requests. I have amended the code to now reflect the following:

suite('<custom-component>', function () {
    var testElement;
    var server;
    var responseHeaders = {
        json: { 'Content-Type': 'application/json' },
        plain: { 'Content-Type': 'text/plain' }
    };
    setup(function () {
        replace('custom-component').with('fake-custom-component');
        server = sinon.fakeServer.create();
        server.respondWith('GET', /staticFile\.json/, [
            200,
            responseHeaders.json,
            '[{"var1": "9a","var2": "17n"}]'
        ]);
        testElement = fixture("testElement");
    });
    teardown(function () {
        server.restore();
    });
    suite('testSuite', function () {
        // note that I added the "done" as a parameter on my test function
        test('test1', function (done) {
            var ajax = testElement.getElementsByTagName('iron-ajax')[0];
            ajax.generateRequest();
            server.respond();

            // note that I added an async event listener here, and moved my test inside.
            ajax.addEventListener('response', function(e) {
                assert(e.target.lastResponse.var1 === "9a");
                done();
            }
        });
    });
});

I am now able to properly intercept the response, and the test is mostly working as expected. I still have the problem that the iron-ajax's lastResponse is not set, but that is a different question I think.



回答3:

As a component test I believe you should really be testing the component behaviour rather than sinon and/or the iron-ajax element internals. Your request will most probably update some property which will trigger some DOM updates.

I've also always hated setTimeouts in tests, and just found out WCT has a flush method for those cases (it awaits for all async stuff to finish) - it's just not documented on the AJAX testing section, but on the DOM manipulation instead (https://www.polymer-project.org/1.0/docs/tools/tests#test-local-dom).

As such, my approach is:

suite('testSuite', function () {
  test('test1', function (done) {
    // assuming iron-ajax with auto
    server.respond();
    flush(function () {
      expect(el.myObject).to.deep.equal(responseObj);

      // in case you're binding the last response to a property
      expect(el.lastResponse).to.deep.equal(responseObj);

      done();
    })
  });
});