Mocking elements in unit-testing

4,792 views
Skip to first unread message

pie.o...@gmail.com

unread,
Nov 17, 2014, 3:47:15 PM11/17/14
to polym...@googlegroups.com
I have looked into Polymer a lot and plan to start a small project with it soon.
However, I'm really concerned with testing as I would like to build my components
by the FIRST principle http://www.addyosmani.com/firs/index.html. While
Polymer seem to solve the first four (Focused, Independent, Reusable, Small)
very good but it seems to struggle with the last one (Testable).

While web-component-tester provides a way of unit-testing components it doesn't
seem to provide the answer to Mocking and thus very much limits the testability
of components.

Mocking HTTP, Websocket, Geolocation and similar is crucial but it would also
be very good to be able to mock sub components so that tests of components
that composit other components doesn't become integration tests.

Is there anything I have missed or is this a missing part in Polymer and Web Components?

kald...@gmail.com

unread,
Nov 18, 2014, 10:38:51 AM11/18/14
to polym...@googlegroups.com, pie.o...@gmail.com
This is a concern I have encountered as well. Especially once a component composes other components, it becomes very difficult to understand the best approach for mocking.

In a project I'm working on, one component is responsible for API calls. (Let's call it <example-api>). Another component depends on <example-api> by composing it. So we have a composition like:

<polymer-element name="example-component">
  <template>
    <example-api></example-api>

     <!-- other code -->
  </template>
</polymer-element>

The problem this presents is that whenever I want to write a test for example-component, and I add an example-component to the page, it's going to add an example-api component as well, and if that component does any XHRs on load, I have no way to interrupt or redirect them. I want to be able to create an example-component with a stubbed version of example-api, but don't know how to do that.

This is definitely an area where Polymer needs more discussion, documentation, and guidance.

Feedback and input very welcome.

Eric Bidelman

unread,
Nov 18, 2014, 11:28:41 AM11/18/14
to kald...@gmail.com, polymer-dev, pie.o...@gmail.com
We definitely need better examples and articles around testing and using mock services.

Until then, check out Scott's POC for mocking core-ajax and the complimentary way one can do mock data using components.

We've also had luck using SinonJS for Google Web Components to stub out live API calls. Example here.

Hope this helps!

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/polymer-dev/d2723c0e-c38e-49ab-8ff5-a24b013cdca6%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

dane.o...@gmail.com

unread,
Nov 18, 2014, 11:56:32 AM11/18/14
to polym...@googlegroups.com, kald...@gmail.com, pie.o...@gmail.com
I'm not sure this is just a documentation thing as it doesn't look like there's enough hooks for Andrew's example. I've run into the same issue and have yet to solve it.

The basic problem seems to be 3 fold:
 - elements cannot be redefined once they're registered.
 - by using declarative syntax, our element dependencies are hard coded in HTML
 - there's no way to reference dependent elements before they're loaded, thus your first chance to stub is often too late

What we really need is a way to downgrade elements for the duration of a test. If we had this, consumers like Andrew could fire events from `example-api` and setup expected attributes without going through lifecycle methods, templates, binding, etc.

Is this even possible today?

I've been working on a mechanism to do this. It involves monkey patching `document.createElement` method and playing with prototypes as they're being registered to make them aware of a `stubbed` mode. It hasn't been fruitful yet (though I'm still moderately hopeful).

Any thoughts?

Ian MacLeod

unread,
Nov 18, 2014, 2:30:35 PM11/18/14
to dane.o...@gmail.com, polym...@googlegroups.com, kald...@gmail.com, pie.o...@gmail.com
Yeah, it's not a great situation - we may end up getting some help from the platform (unregistering or overriding elements - but there's a lot of design considerations there: like what happens to already instantiated elements?). Plus that'll be a while.

There are a few hooks we can make use of now, though:

document.createElement()/Polymer(): Like Dane's experiments here, seems fruitful to hook at this point. I wonder if it might be easier to declare your stubs before importing the elements (and just swapping out the definition entirely). Would end up with more .html suites, but seems decent.

Polymer.getRegisteredPrototype()/HTMLElement.getPrototypeForTag(): (for non-polymer elements it does a Object.getPrototypeOf(document.createElement(tag))). Alternatively, we could use sinon to stub/mock out prototypes. Getting that to occur at the correct time will likely be painful - especially when supporting polyfills vs not. Lots of control here, though.

Rasmus

unread,
Nov 18, 2014, 2:52:55 PM11/18/14
to Ian MacLeod, dane.o...@gmail.com, polym...@googlegroups.com, kald...@gmail.com
mock-ajax seems very interesting, if it's working every time (I.E: doesn't depend on that the browser downloads the other element slower) it feels like a great start.
With some wrappers around that one could get a pretty pleasant testing experience.


>Getting that to occur at the correct time will likely be painful - especially when supporting polyfills vs not. Lots of control here, though.
Could Polymer or web-component-tester provide some nice hook that is called on the correct time?
--
Rasmus

Max

unread,
Nov 19, 2014, 11:21:44 AM11/19/14
to pie.o...@gmail.com, polym...@googlegroups.com

I'm interested in this too.

I wonder if there's a solution by setting a for the test, so that the imports load from a different place from the real implementation, where the sub-components are instead mocked.

cletusw

unread,
Dec 15, 2014, 12:22:30 PM12/15/14
to polym...@googlegroups.com, pie.o...@gmail.com
Has anyone looked into using ServiceWorker (https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API/Using_Service_Workers) for testing? It seems to me this would allow you to instruct the browser to load your mocked versions of dependencies without changing your element being tested.

Dane O'Connor

unread,
Dec 16, 2014, 8:02:07 AM12/16/14
to cletusw, polym...@googlegroups.com, pie.o...@gmail.com
I've implemented a strategy for this and it's working for us at the moment. Check it out here: https://github.com/thedeeno/rs-container

The project lets you temporarily 'downgrade' custom elements into simple HTMLElements. This means your components won't go through their full lifecycle or load their dependencies, etc. Perfect for stubbing. My project lets you do this declaratively or imperatively. Check it out. It'd be great to see how it works out for you. 

Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to a topic in the Google Groups "Polymer" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/polymer-dev/31W69ZE1Wng/unsubscribe.
To unsubscribe from this group and all its topics, send an email to polymer-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/polymer-dev/7c45489b-3cdd-4d3a-9ec1-b224d552f702%40googlegroups.com.

Clayton Watts

unread,
Dec 16, 2014, 11:48:27 AM12/16/14
to Dane O'Connor, polym...@googlegroups.com, pie.o...@gmail.com
This looks nice, but it wouldn't stop elements from loading dependencies using HTML Imports and those dependencies from loading their dependencies, etc., right?

Max

unread,
Dec 16, 2014, 11:57:25 AM12/16/14
to Clayton Watts, Dane O'Connor, polym...@googlegroups.com, pie.o...@gmail.com
Wild thought from a newbie: would it not be possible to simply use a different <base> so that it loads the dependencies from a location where they *are* mocked? Perhaps you could import the unit under test first, then each mock could test if one is already registered and if so not register itself.

Max.

You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/polymer-dev/CAGw__zGBzPcSV-%2Bgyx_UKtgAxwj5jH17xPx0RPObAFeLStmDtQ%40mail.gmail.com.

Dane O'Connor

unread,
Dec 16, 2014, 1:30:05 PM12/16/14
to Max, Clayton Watts, polym...@googlegroups.com, Rasmus Eneman
@Clayton It doesn't stop the browser from importing the whole graph, yes, but that's fine. Your dependent components will be registered, but until they're actually instantiated they don't do anything.

@Max that's actually a much more difficult way to solve the problem, imo. It also requires more boiler plate to stub components as you need to create 'fake elements' and serve them from your application someplace. I'd rather not change my dependency graph at all or rewrite any of my production code just to do unit testing. Instead, I think it makes more sense to simply stub out dependencies when necessary. This strategy helps me do that.


asmod...@gmail.com

unread,
May 5, 2019, 10:42:13 AM5/5/19
to Polymer
There is another problem, you can not have Hot Module Replacement working with web components because calling customElements.define('my-element', MyElement); second time will result in an error, rather than show updated component view.
This is a deal killer for many developers.
import MyElement from "../test-component";

customElements.define('my-element', MyElement);

if (module.hot) {
   module.hot.accept('../test-component', function() {
       console.log('Accepting the updated testComponent module!');
       customElements.define('my-element', MyElement);
   });
}
Reply all
Reply to author
Forward
0 new messages