Mocking child components during component testing


#1

Given an example:

// my-view-model.html
<require from="./foo"></require>
<template>
    <foo>
    </foo>
</template>
// foo.html
<require from="./bar"></require>
<template>
    <bar>
    </bar>
</template>

I want to be able to write a component test for <foo>. My thinking is I want to test:

  • it does the correct thing when data is bound into it
  • it does the correct things when I interact with it
  • it does the binds the data correctly into any child components
  • it responds to change from the child components properly

What I’m trying to avoid is my test for <foo> knowing any implementation detail of <bar>. Currently, when I test foo, I use the StageComponent to create it and can control the lifecycle etc:

component = StageComponent
      .withResources(PLATFORM.moduleName('foo'))
      .inView('<foo></foo>');

The problem is that this will actually create a <bar> as well, because this is imported with <require>. In a simple case where <bar> has no dependencies, this is probably not much of an issue in most cases, however it may have a dependency on something that is unavailable - for example, it has a Fetch client injected into it - but Fetch is not available in this runtime (Jest). Whilst I can replace the HttpClient (Fetch) instance in the container with a Mock, this now leaks implementation detail of the child components, which is not the component I’m intending to test etc.

Likewise, I’d like to be able to assert that the child component had the correct data bound into it, but I don’t care how it uses that data - that is what the test for that child component would do. Furthermore, I’d like to assert that component under test responds to events dispatched by the child components, but I think that is currently achievable in the test by finding the element for the component and dispatching an Event onto it.

So my thinking is ideally I’d be able to replace the element entirely with a complete mock. I’ve had success manually registering an Element during bootstrapping like so:

@noView()
@customElement('bar')
class FakeBar {
}

let behaviour = metadata.getOwn(metadata.resource, FakeBar);
behaviour.initialize(aurelia.container, FakeBar)
aurelia.resources.registerElement('bar', behaviour);

However, this only works when i remove the <require></require> statement in the original template, presumably the require is being processed afterwards and redeclaring the custom element.

I’d like to raise this discussion to:

  • understand whether my reasoning is sound behind the testing methodology;
  • how other people may have achieved this; and
  • how I can achieve this particular approach (if it is sound)

Cheers!
Tom


#2

@bigopon werent we looking into this? Remember you found a workaround for the templating issue we had, something along wrong injection.


#3

Yes it 's discussed here https://github.com/aurelia/testing/issues/20

I couldn’t decide what to do when we adding a resources module in withResources. Will it still load the module (a module can contain more than 1 resource), or we need to prevent that too


#4

Well since this is not really a best practice I would scratch that.


#5

Bump - would love to hear from some of the core team on this.