Fonteva built their own test adapters for some base components, such as inputFields, buttons, and message prompts. When Lightning Components call methods (like validate or setOtherAttributes), Fonteva mocks that out.

When you're using adapter mode, you're not actually testing if a component or attribute run correctly. This is because setOtherAttributes exists inside of another managed package instead of in your code. That managed package is only using your code.

EventRegistrationFlowDetailsControllerSpec.js

const { frameworkAdapters } = require('mocha-fonteva-adapters');
JS

You can create your own adapters. Fonteva provides the following adapters at https://github.com/Fonteva.mocha-fonteva-adapters/master/lib/adapters:

For example, buttonAdapter.js shows all the parameters and methods that will be stubbed out.

The code block below contains the full EventRegistrationFlowDetailsControllerSpec.js example.

In line 111, a Framework Button is added. Then on line 115, when you add cancelAttendeeSetup and component.find('cancelAttendeeSetup') you get the mocked version of the Framework button. However, it still has all the functionality that your standard component has.

You create a componentFactory for each of the component.find attributes. Additionally, you have a componentFactory for the actual component object (line 112). If you don't pass in the second parameter (lines 14-17), the testing framework is going to think the mocked component an Aura component.

EventRegistrationFlowDetailsControllerSpec.js

    @testMethod('should call addtickettype')
    @inject(LTEEvent)
    addNewAttendee(eventObj) {
        let button = componentFactory({}, 'Framework:Button');
        let firstName = componentFactory({}, 'Framework:InputFields');
        const component = componentFactory({
            eventObj,
            findMap : {
                cancelAttendeeSetup : button,
                continueAttendeeSetup : button,
                firstName : firstName
            }
        });
        controller.addNewAttendee(component, {}, helper);
        expect(helper.addTicketType).to.have.been.calledWith(component);
    }
JS

 

In line 8 of buttonAdapter.js :

buttonAdapter.js

setParam(componentInstance, 'type', 'brand');
JS


Then, go to EventRegistrationFlowDetailsControllerSpec.js and add type = 'testing'.

EventRegistrationFlowDetailsControllerSpec.js

let button = componentFactory({type = 'testing'}, 'Framework:Button');
JS


This means that you want to instantiate the button component from the adapter. Then set the type attribute to testing instead of the default. This allows you to override values inside of the componentFactory on the fly.

To test an input field when your testing validation has failed, you can set the validated attribute to false in the Framework inputFields component. Even though validated is set to true by default, when component.find on cancelAttendeeSetup.validate is returned, you'll get a false value.

EventRegistrationFlowDetailsControllerSpec.js

let firstName = componentFactory({validated : false}, 'Framework:InputFields');
JS

Setting up a Component

To set up the component, (lines 112-118), you call the controller and the helper method (line 119) and stub out the entire helper. The helper never calls the controller; the controller always calls the helper. Additionally, a helper never stubs out a controller, but controllers stub out the helper.

EventRegistrationFlowDetailsControllerSpec.js

    @testMethod('should call addtickettype')
    @inject(LTEEvent)
    addNewAttendee(eventObj) {
        let button = componentFactory({}, 'Framework:Button');
        let firstName = componentFactory({validated : false}, 'Framework:InputFields');
        const component = componentFactory({
            eventObj,
            findMap : {
                cancelAttendeeSetup : button,
                continueAttendeeSetup : button,
                firstName : firstName
            }
        });
        controller.addNewAttendee(component, {}, helper);
        expect(helper.addTicketType).to.have.been.calledWith(component);
    }
JS
  • Because your controllers are instantiated once for each component of the same type, attempt to keep your controllers as lean as possible.
  • Since the helper is instantiated once for all component types, the logic for your controllers should mostly be in the helper


When you're testing or writing your code, your helper should be in discrete blocks of code that do atomic actions. We don't recommend creating a helper method that does a large number of tasks because it is hard to test. If you have a helper method that does a high number of tasks, break that helper method into multiple methods: one that wraps them all, and then the other individual methods that get called in the wrapped method. This way, you can test each individual method separately.

 

In line 136, we use the expect() library so you can make sure component.find('cancelAttendeeSetup').stopIndicator actually got called.

EventRegistrationFlowDetailsControllerSpec.js

expect(component.find('cancelAttendeeSetup').stopIndicator).to.have.been.called;
JS


If you have a button you want to interact with, the Framework button is something that you can use component.find with. You use componentFactory() (line 111) to generate a mock of the Framework button because you're sending an empty object.

EventRegistrationFlowDetailsControllerSpec.js

    @testMethod('should call addtickettype')
    @inject(LTEEvent)
    addNewAttendee(eventObj) {
        let button = componentFactory({}, 'Framework:Button');
        const component = componentFactory({
            eventObj,
            findMap : {
                cancelAttendeeSetup : button,
                continueAttendeeSetup : button
            }
        });
        controller.addNewAttendee(component, {}, helper);
        expect(helper.addTicketType).to.have.been.calledWith(component);
    }
JS


You can either send an empty list or send, for example, enableProgressIndicator : false. Additionally, you can send any of the attributes that the component supports.

EventRegistrationFlowDetailsControllerSpec.js

let button = componentFactory({enableProgressIndicator : false}, 'Framework:Button');
JS

You've now created a mock component object. If you send that component as is, the SalesForce component.find will fail because that component doesn’t actually exist. To remedy this, Fonteva added an object to the component factory called findMap (line 114). findMap provides the component with actual aura IDs. So now when you do a component.find('cancelAttendeeSetup') (line 115), you will get an instance of a mocked Framework:Button (line 111). This allows you to test attributes such as stopIndicator and startIndicator—part of the button component—outside of SalesForce. Through this mechanism, Fonteva allows you to have a fully mocked, working component to test.

For example: 

EventRegistrationFlowDetailsControllerSpec.js

expect(component.find('cancelAttendeeSetup').to.have.been.calledWith('stopIndicator')
JS

 

Here is another way to use stopIndicator:

EventRegistrationFlowDetailsControllerSpec.js

expect(component.find('cancelAttendeeSetup').stopIndicator().to.have.been.called);
JS


The above code verifies that stopIndicator was actually called. Based on the response, you can verify that your conditional log is firing and that it stopped the button indicator.

 

Previous: Stubbing Out     |     Next: Data Generators