Testing polymer 1.0 components with iron-ajax usin

2019-07-17 19:40发布

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?

3条回答
劳资没心,怎么记你
2楼-- · 2019-07-17 19:51

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.

查看更多
beautiful°
3楼-- · 2019-07-17 19:55

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();
                });
            });
查看更多
Deceive 欺骗
4楼-- · 2019-07-17 20:12

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();
    })
  });
});
查看更多
登录 后发表回答