Lightning Testing Service with Jasmine

Lightning Testing Service:  Salesforce has already provided the Apex Testing framework to check the features in your Apex classes and triggers at the server side. Now, the question is how to test the Lightning Component at the client side.
Lightning Testing Service is the solution to test Lightning Component with behavior driven javascript framework Jasmine or Mocha.

In this tutorial, we will learn how to use Lightning Testing Service using the Jasmine framework. First of all, we will little bit discuss Jasmine to understand basic functionalities.

1. Jasmine has the test suite containing specific test functions using describe() function.
2. it() determines the specs means it() contains to write the actual test scenario and expectations.
3. An expectation is nothing but an assertion which will return true or false.

Step 1: Install the unmanaged package of LTS from here. Please click on LTS-Jasmine. It will open Salesforce Login UI. Please substitute the  URL to login from test, if you want to install in your developer/ production org.

Step 2:  Now, you can see the following files are uploaded to Static Resource.

lts_jasmine (zip file)- containing core Jasmine libraries.
lts_jasmineboot (javascript file)- performing all of the necessary initialization before executing the loaded environment.
lts_testutil (javascript file)- defines utility ($T) object which will use to run the spec for your lightning component.

Now, you can see one Lightning Component named lts _jasmineRunner is installed. Thanks to Salesforce to provide all these files to run Lightning Testing Sevice.

Step 3: Now we will create a javascript file called testSuite.js and upload to Static Resource named testSuite. So, in this testSuite, we will write our test specs. First, we will check Jasmine is working or not.

describe("Lightning Testing Service", function(){

	
	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
});

Step 4: Need to create a Lightning app named testSuiteApp.app where we can call the packaged Lighntning Component named lts_jasmineRunner.cmp with our testSuite file just like

<aura:application>
	<c:lts_jasmineRunner testFiles="{!$Resource.testSuite}" />
</aura:application>

Now Run the Lightning App from developer console and you will get this type of screen:

However, you can create a Lightning Component Tab with Lightning Component instead of Lightning App.

Step 5: WOW, you have done a great job. Now, just check what you have written in the testSuite file and the result and again read from Step 1 to Step 4.
So, we have learned how to run Jasmine in Salesforce. Now, we will start to learn how to write tesSuite for Lightning Component.

Step 6: Okay, so we will create a Lightning Component named “LTSPanel” and place this to Lightning App Page(by Lightning App Builder) to show the output. From now, we will continue to update the Lightning Component and testSuite file to execute the Lightning Test Service.

Step 7: To show the welcome message.

LTSPanel.cmp | to show welcome message

<aura:component implements="flexipage:availableForAllPageTypes">	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
	</lightning:card>
</aura:component>

Step 8: You will get the following output by running the Lightning Page Tab named “LTS Panel”.

Step 9: Objective: To check the static value from Lightning Component by HTML Tag.
Now, We will update the test suite file from Line no. 21 – 35.

describe("Lightning Testing Service", function(){

	
	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	describe('c:LTSPanel', function() {

         /**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });
     });
	
});

Please note this describe() is responsible to consider the respective test suite. it() contains the test suite where $T.createComponent() is calling the respective Lightning Component named “LTSPanel” to run the test case. You need to use a namespace on Lightning Component just like lightningpro:LTSPanel (where lightningpro is the namespace) if you have set the namespace for your organization.

$T.createComponent() takes three parameters such as Component Name, to set Component’s attributes, defines if the test is required when the component to be rendered [later we will discuss about the third parameter in detail].

expect() verifies the actual result with respect to the expected result. In this example, we have tried to access the value from the component by using native HTML tag such as document.getElementById(“welcomeMSGId”).textContent and compare with function toContain() which has the expected result.

Step 10: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

Note that, “Spec 1: to test Welcome Message” is printed from it() which we have written in the previous step.

Step 11: Now we will update the same Lightning Component where we will place a button named “Show All Contacts”. If the button is placed, one panel with the message will be opened at this point in time.

LTSPanel.cmp | to show the panel with the message when a button will be pressed

<aura:component implements="flexipage:availableForAllPageTypes">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
		
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!v.showOutput}">			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
				<label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
			</div>
			<aura:set attribute="else">				
				<div style="padding:0.5rem;background: rgb(22, 50, 92);">
					<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
				</div>
			</aura:set>
		</aura:if>
	</lightning:card>
</aura:component> 

LTSPanelController.js | to set the Boolean variable as True

({
    showContacts : function(component, event, helper) {
        component.set("v.showOutput", true);
     },
)}

Step 12: Now run the same tab named “LTS Panel” to show the current output. The following result is after clicking the button named “Show All Contacts”.

Step 13: Objective: To test the show/hide panel when the button is pressed through LTS.
Now, we will update the same testSuite file within the same describe(“c: LTSPanel”) function.

describe("Lightning Testing Service", function(){

	
	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("c:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });
	}); 	
});

By default the Panel is hidden that’s why this line expect(component.find(“truePanelId”)).toBeFalsy() helps to pass the test case. Now, we have clicked the button programmatically by getEvent(“press”).fire(). But, this is applicable for the only ui: button, this is not applicable for lightning: button. Now, as per Lightning component after pressed the button the respective Panel(aura: id=”truePanelId”) would be opened. So, this line expect(component.find(“truePanelId”)).toBeTruthy() returns true to pass the test case.

Step 14: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

Step 15: Now, we will test the Server Method invocation by Lightning Test Service. So, for that, we will write Apex Classes as before mentioned in the previous blog.

Selector Class: ContactSelector.apxc

public with sharing class ContactSelector {
	public static List<Contact> selectRecords(){
		return [SELECT Id,
					   Name,
					   AccountId,
					   Department,
					   lightningpro__Level__c,
					   Birthdate,
					   Email,
					   MobilePhone
					   FROM Contact LIMIT 5];
	}
}

Controller: LTSPanelController.apxc

public with sharing class LTSPanelController {
	
	@AuraEnabled
	public static List<Contact> getContacts(){
		return ContactSelector.selectRecords();
	}
}

Step 16: Now, we will update our LTSPanel component bundle.

LTSPanel.cmp | to show the Contact records

<aura:component implements="flexipage:availableForAllPageTypes" controller="LTSPanelController">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
	<aura:attribute name="contactObj" type="Object"/>
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!and(v.showOutput, v.showSpinner)}">				
			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
                <label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
            </div		    
			<div aura:id="spinnerId" class="slds-spinner_container">		       
				<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
					<span class="slds-assistive-text">Loading</span>		         
					<div class="slds-spinner__dot-a"></div>		         
					<div class="slds-spinner__dot-b"></div>
		       </div>
		    </div>
	 		<aura:set attribute="else">			
					<aura:if isTrue="{!v.contactObj.length>0}">						
						<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="contactsPanelId">
							<label class="slds-align_absolute-center slds-text-color_inverse">THANKS APEX, NOW I AM ABLE TO SHOW ALL CONTACTS</label>
						</div>	
						<lightning:datatable data="{!v.contactObj}" 
											 columns="{!v.contactColumns}" 
											 keyField="Id" 
											 hideCheckboxColumn="true" 
											 onrowaction="{!c.handleRowAction}" />
						<aura:set attribute="else">								
							<div style="padding:0.5rem;background: rgb(22, 50, 92);">
								<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
							</div>
						</aura:set>
					</aura:if>
			</aura:set>				
		</aura:if>
	</lightning:card>
</aura:component> 

LTSPanelController.js

({
	showContacts : function(component, event, helper) {
        component.set("v.showOutput", true);
		helper.performShowingContacts(component);
	},
})

LTSPanelHelper.js

({
	performShowingContacts : function(component) {
		var action = component.get("c.getContacts");
		this.setupPerformShowingContactsAction(component, action);
		$A.enqueueAction(action);
	},

	setupPerformShowingContactsAction : function(component, action) { 

		action.setCallback(this, function(response){
        	this.handlePerformShowingContactsResultsCallBack(component, response)
        });	
	},

	handlePerformShowingContactsResultsCallBack : function(component, response) {

		var responseState = response.getState();
		var responseError = response.getError();
		var responseValue = response.getReturnValue();

		switch(responseState){

			default: break;
			case 'NEW': break;
			case 'RUNNING': break;

			case 'SUCCESS':
				
				component.set('v.contactColumns', 
				[
					{label: 'Contact name', fieldName: 'Name', type: 'text'},
					{label: 'Department', fieldName: 'Department', type: 'text'},
					{label: 'BirthDate', fieldName: 'BirthDate', type: 'datetime'},
					{label: 'Contact Email', fieldName: 'Email', type: 'email'},
					{label: 'Contact Phone', fieldName: 'MobilePhone', type: 'phone'}				
            	]);
				component.set("v.contactObj", responseValue);
				component.set("v.showSpinner", false);
				break;

			case 'INCOMPLETE': break;
			case 'ERROR': 
				console.log('Response Error-->'+JSON.stringify(responseError));
				break;

		}
	}
})

Step 17: You will get the following output by running the Lightning Page Tab named “LTS Panel” after clicking on “Show Contacts” button.

Step 18: Now, we will see the Server method invocation to check Show Contacts by Lightning Test Service in multiple ways such as
1. Testing by Real-Time Server Method Invocation
2. Testing by the creation of data externally
3. Testing by Jasmine Spy function

Step 19: Testing by Real-Time Server Method Invocation:: This process is discouraged because invoking server actions is time-consuming and you may get the timeout error. Let’s see our testSuite file by updating from Line no. 53-69.

describe("Lightning Testing Service", function(){

	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });


        /**
        * Real Time Server Method Invocation
        **/
        it('Step 3: to verify real time server method invocation', function (done) {
        $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function (component) {
            expect(component.get("v.contactObj")).toBeNull();
            component.find("showContactsBtnId").getEvent("press").fire();            
                return $T.waitFor(function () {                    
                    //expect(component.get("v.contactObj").length).toBe(5);                    
                }) 
            }).then(function (component) {
                done();
            }).catch(function (e) {
                done.fail(e);
            });
    	});
	});
});

Step 20: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

This spec interacts with the component to call server side action and waits for the action callback to update. During this operation, you have received the error as Timed Out after waiting 3000ms. You can check the defined default time(3000ms) for the call out in “lts_testUtil” file. You can define the WINDOW.JASMINE_DEFAULT_TIMEOUT_INTERVAL in your testSuite file to resolve this issue. But, there is no guarantee. Line no. 62 is commented out. In the next step, we will check. So, we will comment this spec from Line no.56-69.

Step 21: Need to update the Lightning Component Bundle, LTSPanel.

LTSPanel.cmp | define methods to invoke server Side action for LTS

<aura:component implements="flexipage:availableForAllPageTypes" controller="LTSPanelController">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
	<aura:attribute name="contactObj" type="Object"/>
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
	
	 <!-- for Lightning Test Service -->
    <aura:method name="searchContacts" action="{!c.searchContactsCallback}">
      <aura:attribute name="response" type="Object" />
    </aura:method>
    <!-- /end -->
	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!and(v.showOutput, v.showSpinner)}">				
			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
                <label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
            </div		    
			<div aura:id="spinnerId" class="slds-spinner_container">		       
				<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
					<span class="slds-assistive-text">Loading</span>		         
					<div class="slds-spinner__dot-a"></div>		         
					<div class="slds-spinner__dot-b"></div>
		       </div>
		    </div>
	 		<aura:set attribute="else">			
					<aura:if isTrue="{!v.contactObj.length>0}">						
						<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="contactsPanelId">
							<label class="slds-align_absolute-center slds-text-color_inverse">THANKS APEX, NOW I AM ABLE TO SHOW ALL CONTACTS</label>
						</div>	
						<lightning:datatable data="{!v.contactObj}" 
											 columns="{!v.contactColumns}" 
											 keyField="Id" 
											 hideCheckboxColumn="true" 
											 onrowaction="{!c.handleRowAction}" />
						<aura:set attribute="else">								
							<div style="padding:0.5rem;background: rgb(22, 50, 92);">
								<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
							</div>
						</aura:set>
					</aura:if>
			</aura:set>				
		</aura:if>
	</lightning:card>
</aura:component> 

Please check we have introduced method called “searchContacts” at Line No. 10 to invoke Server Action.

LTSPanelController.js | to call Server Action

({
	showContacts : function(component, event, helper) {
        component.set("v.showOutput", true);
		helper.performShowingContacts(component);
	},

    searchContactsCallback : function(component, event, helper) {
       helper.handlePerformShowingContactsResultsCallBack(component, event.getParam('arguments').response);        
    },
})

Step 22: Now, we will update the testSuite file. You can check from Line No. 71-101.

describe("Lightning Testing Service", function(){

	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });


        /**
        * Real Time Server Method Invocation
        **/
        /*it('Step 3: to verify real time server method invocation', function (done) {
        $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function (component) {
            expect(component.get("v.contactObj")).toBeNull();
            component.find("showContactsBtnId").getEvent("press").fire();            
                return $T.waitFor(function () {                    
                    //expect(component.get("v.contactObj").length).toBe(1);                    
                }) 
            }).then(function (component) {
                done();
            }).catch(function (e) {
                done.fail(e);
            });
    	});*/

    	/**
        * Testing by the creation of data externally
        **/
    	it('Step 4: provided the data when action callback is invoked directly', function(done) {
	        $T.createComponent("lightningpro:LTSPanel", null, true)
		        .then(function(component){
		            // Mocking out the server-side action response
		            var res = {
		                            getState : function(){
		                                return "SUCCESS";
		                            }, 
		                            getError : function(){
		                                return "ERRORS";
		                            }, 
		                            getReturnValue: function(){
		                                return [{"Name":"Contact 1"},{"Name":"Contact 2"}];
		                            }
		                      };
		            component.searchContacts(res);

		            // Assert using components interface
		            expect(component.get("v.contactObj").length).toBe(2);
		            expect(component.get("v.contactObj")[0]['Name']).toContain("Contact 1");

		            // Assert using DOM element owned by the namespace
		            //expect(component.find("contactListId").getElement().children.length).toBe(2);
		            done();
		        }).catch(function(e) {
		            	done.fail(e);
		           });
	    });

	});	
	
});

Step 23: Now, please run the testSuiteApp.app again from developer console and you will get this type of output.

You can check Spec 3 is missing because we have commented Real Time server method action from the testSuite file. You can check Line No. 78 where we have declared a variable called “res” in where we have virtually created the Contact records(Line No. 86) and defined getState and getError functions because those functions are declared in LTSPanelHelper.js(Line No. 17-18). No, we are calling “searchContacts” method of the LTSPanel component with defined “res” object. If all the actions are successful, we can compare the length of the defined “contactObj” and Name of any instance of “contactObj” by Jasmine expect() function (Line No. 92-93).

Step 24: We will test the same thing by Jasmine SPY function. For this, we need to update Lightning Component, LTSPanel with standalone method with no attributes.

LTSPanel.cmp | to declared “loadContacts” method

<aura:component implements="flexipage:availableForAllPageTypes" controller="LTSPanelController">
	
	<aura:attribute name="welcomeMessage" type="String" default="Good Morning! My Friend"/>
	<aura:attribute name="showOutput" type="Boolean" default="false"/>
	<aura:attribute name="contactObj" type="Object"/>
    <aura:attribute name="contactColumns" type="List"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
	
	 <!-- for Lightning Test Service -->
    <aura:method name="searchContacts" action="{!c.searchContactsCallback}">
      <aura:attribute name="response" type="Object" />
    </aura:method>
	<aura:method name="loadContacts" action="{!c.showContacts}"/>
    <!-- /end -->
	
	<lightning:card title="">
		<aura:set attribute="title">			
			<div id="welcomeMSGId">
				{!v.welcomeMessage}
			</div>
		</aura:set>
		<aura:set attribute="actions">
			<ui:button label="Show All Contacts" press="{!c.showContacts}" aura:id="showContactsBtnId"/>
		</aura:set> 
		<aura:if isTrue="{!and(v.showOutput, v.showSpinner)}">				
			
			<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="truePanelId">
                <label class="slds-align_absolute-center slds-text-color_inverse">WAIT, I WILL SHOW THE DATA WHEN MY FRIEND APEX WILL SEND ME THE RESPONSE</label>
            </div		    
			<div aura:id="spinnerId" class="slds-spinner_container">		       
				<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
					<span class="slds-assistive-text">Loading</span>		         
					<div class="slds-spinner__dot-a"></div>		         
					<div class="slds-spinner__dot-b"></div>
		       </div>
		    </div>
	 		<aura:set attribute="else">			
					<aura:if isTrue="{!v.contactObj.length>0}">						
						<div style="padding:0.5rem;background: rgb(22, 50, 92);" aura:id="contactsPanelId">
							<label class="slds-align_absolute-center slds-text-color_inverse">THANKS APEX, NOW I AM ABLE TO SHOW ALL CONTACTS</label>
						</div>	
						<lightning:datatable data="{!v.contactObj}" 
											 columns="{!v.contactColumns}" 
											 keyField="Id" 
											 hideCheckboxColumn="true" 
											 onrowaction="{!c.handleRowAction}" />
						<aura:set attribute="else">								
							<div style="padding:0.5rem;background: rgb(22, 50, 92);">
								<label class="slds-align_absolute-center slds-text-color_inverse">NO  CONTACT(S)  FOUND</label>
							</div>
						</aura:set>
					</aura:if>
			</aura:set>				
		</aura:if>
	</lightning:card>
</aura:component> 

Step 25: Now, we will update testSuite file with Jasmine SPY function from Line No. 104-136.

describe("Lightning Testing Service", function(){

	afterEach(function(){
		// Each spec (test) renders its components into the same div,
        // so we need to clear that div out at the end of each spec.
		$T.clearRenderedTestComponents();
	})

	/*** JUST CHECKING JASMINE ***/

	describe("A test suite that tests the obvious", function() {
	  it("spec that verifies that true is true", function() {
	    expect(true).toBe(true);
	  });
	  it("spec that verifies that false is false", function() {
	    expect(false).toBe(false);
	  });
	});

	
	describe('c:LTSPanel', function() {

		/**
	    * First, we are checking HTML Content using native tag document.getElementByid()
	    **/
	    it("Spec 1:  to test Welcome Message ", function(done) {
	        $T.createComponent('lightningpro:LTSPanel',null,true)
	            .then(function(component) {
	            	expect(document.getElementById("welcomeMSGId").textContent).toContain("Good Morning! My Friend");
	            	done();
	            }).catch(function (e) {
	                done.fail(e);
	            });
	    });

	    /**
	    * We are checking show or hide panel by toBeTruthy() or toBeFalsy() function
	    **/
	    it("Spec 2:  to check the state of output panel", function(done) {
            $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function(component) {
                //component.set("v.showContacts", true);
                expect(component.find("truePanelId")).toBeFalsy();
                component.find("showContactsBtnId").getEvent("press").fire();
                expect(component.find("truePanelId")).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });


        /**
        * Real Time Server Method Invocation
        **/
        /*it('Step 3: to verify real time server method invocation', function (done) {
        $T.createComponent("lightningpro:LTSPanel", null, true)
            .then(function (component) {
            expect(component.get("v.contactObj")).toBeNull();
            component.find("showContactsBtnId").getEvent("press").fire();            
                return $T.waitFor(function () {                    
                    //expect(component.get("v.contactObj").length).toBe(1);                    
                }) 
            }).then(function (component) {
                done();
            }).catch(function (e) {
                done.fail(e);
            });
    	});*/

    	/**
        * Testing by the creation of data externally
        **/
    	it('Step 4: provided the data when action callback is invoked directly', function(done) {
	        $T.createComponent("lightningpro:LTSPanel", null, true)
		        .then(function(component){
		            // Mocking out the server-side action response
		            var res = {
		                            getState : function(){
		                                return "SUCCESS";
		                            }, 
		                            getError : function(){
		                                return "ERRORS";
		                            }, 
		                            getReturnValue: function(){
		                                return [{"Name":"Contact 1"},{"Name":"Contact 2"}];
		                            }
		                      };
		            component.searchContacts(res);

		            // Assert using components interface
		            expect(component.get("v.contactObj").length).toBe(2);
		            expect(component.get("v.contactObj")[0]['Name']).toContain("Contact 1");

		            // Assert using DOM element owned by the namespace
		            //expect(component.find("contactListId").getElement().children.length).toBe(2);
		            done();
		        }).catch(function(e) {
		            	done.fail(e);
		           });
	    });


	    /**
        * Server side action Testing by Jasmine SPY function
        **/
	    it('Step 5: SPY - FUNCTION:: updates with provided data when action callback is invoked directly- spy ', function(done) {
	        $T.createComponent("lightningpro:LTSPanel", null, true)
	        .then(function(component){
	            // Mocking out the server-side action response
	            var res = {
	                            getState : function(){
	                                return "SUCCESS";
	                            }, 
	                            getError : function(){
	                                return "ERRORS";
	                            },
	                            getReturnValue: function(){
	                                return [{"Name":"Contact 1"},{"Name":"Contact 2"}];
	                            }
	                      };

	            spyOn($A, "enqueueAction").and.callFake(function(action) {
	                    var cb = action.getCallback("SUCCESS")
	                    cb.fn.apply(cb.s, [res]);
	                });
	            component.loadContacts();

	            // Assert using components interface
	            expect(component.get("v.contactObj").length).toBe(2);
	            expect(component.get("v.contactObj")[0]['Name']).toContain("Contact 1");
	            done();
	        }).catch(function(e) {
	            done.fail(e);
	        });
    	});

	});	
	
});

Step 26: Please run the testSuite.app file from developer console to see the output.

Please check from the Line no. 123 – 127 of your testSuite file. We have defined Jasmine Spy function at Line no. 123 to satisfy Jasmine’s mocking capabilities. In this process, the biggest advantage is that you don’t need to break the functions into the component’s helper.js file. Only you need to declare the method in the Lightning Component. This approach is suggested to check the server side action by Lightning Test Service.

Step 27: Now, question is that how will we cover event handling testing by Lightning Test Service. In this tutorial, we will not cover event handling in detail. If you have used component event and application event, please update your testsuite file with the following spec.

it('Spec 6: to check Component and Application Event', function(done) {
	$T.createComponent("lightningpro:LTSPanel", null)
	.then(function(component){
		var componentEvent = component.getEvent("cmpEvent");
		componentEvent.setParams({"data":"component event fired"});
		componentEvent.fire()
		expect(component.get("v.message")).toBe("component event fired");
		$T.fireApplicationEvent("lightningpro:appEvent", {"data":"application event fired"});
		expect(component.get("v.message")).toBe("application event fired");
		done();
		}).catch(function(e) {
			done.fail(e);
		});
});

Remember, in this example “lightningpro” is the namespace of my Org. If your org has not any namespache please use “c” instaed of using “lightningpro”.

A big thank you for spending your valuable time to understand the Lightning Testing Service with Jasmine.
                                                                   Again, Thank You.

4,753 total views, 1 views today

Rating: 5.0/5. From 2 votes.
Please wait...
This entry was posted in Lightning Components. Bookmark the permalink.

1 Response to Lightning Testing Service with Jasmine

  1. Rohit Joy says:

    What a fantastic read on Salesforce. This has helped me understand a lot in Salesforce course. Please keep sharing similar write ups on Salesforce. Guys if you are keen to know more on Salesforce, must check this wonderful Salesforce tutorial and i’m sure you will enjoy learning on Salesforce training.:-https://www.youtube.com/watch?v=ZV0-1lCcAbU

    Rating: 5.0/5. From 1 vote.
    Please wait...

Leave a Reply