5

I've written a visualforce page (with the help of examples etc from the internet) to simply display an instance of fullcalendar.js.

There's no buttons etc, it simply loads when clicked from the home tab link/button in salesforce. When the VF page loads, it displays a specific user's events/activities for any user viewing the page. I've tweaked the fullcalendar.js code to display additional details when an event is hovered.

I'm confused as to how to test the controller. The controller basically only gathers the events. I have a test that calls the pageLoad() method - it passes but only covers 15%... I had a lot more in the test but removed code that didn't help the coverage to start over.

Can anyone help me to understand how to initiate the rest of the controller code in the test? Sorry for all of the code copied below - but I've been working on this for about a week and I've finally decided to reach out for HELP! I've not included the fullcalaendar script files at this time (saved as static resources). Please note that this VF page works exactly how I want it to, I just can't seem to test it properly...

Thanks in advance!

VF Page code:

 <apex:page controller="MiroCalendar_Controller02" action="{!pageLoad}">
<link href="{!$Resource.fullCalendarCSS}" rel="stylesheet" />
<link href="{!$Resource.fullCalendarPrintCSS}" rel="stylesheet" media="print" />    

<script src="{!$Resource.JqueryMinJS}"></script>
<script src="{!$Resource.JqueryUICustomMinJS}"></script>
<script src="{!$Resource.MomentMinJS}"></script>
<script src="{!$Resource.fullCalendarJS}"></script>


<script>

    //We need to wrap everything in a doc.ready function so that the code fires after the DOM is loaded
    $(document).ready(function() {
        //Call the fullCallendar method. You can replace the '#calendar' with the ID of the dom element where you want the calendar to go. 
        $('#calendar').fullCalendar({
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            editable: false,
            events:
            [
                //At run time, this APEX Repeat will render the array elements for the events array
                <apex:repeat value="{!events}" var="e">
                    {
                        title: "{!e.title}",
                        description: "{!e.description}",
                        createdBy: "{!e.createdBy}",
                        createdOn: "{!e.createdOnString}",
                        modifiedBy: "{!e.modifiedBy}",
                        modifiedOn: "{!e.modifiedOnString}",
                        start: '{!e.startString}',
                        end: '{!e.endString}',
                        url: '{!e.url}',
                        allDay: {!e.allDay},
                        className: '{!e.className}'
                    },
                </apex:repeat>
            ]
       });                
    });

</script>

<!--some styling. Modify this to fit your needs -->
<style>
    #cal-options {float:left;}
    #cal-legend { float:right;}
    #cal-legend ul {margin:0;padding:0;list-style:none;}
    #cal-legend ul li {margin:0;padding:5px;float:left;}
    #cal-legend ul li span {display:block; height:16px; width:16px; margin-right:4px; float:left; border-radius:4px;}
    #calendar {margin-top:20px;}
    #calendar a:hover {color:#fff !important;}

    .fc-event-inner {padding:3px;}
    .event-miro-corp {background:#aea5cd;border-color:#e93329;}
    .event-miro-bank {background:#c7b676;border-color:#e93329;}
    .event-miro-retail {background:#e93329;border-color:#e93329;}
    .event-miro-other {background:#a19f9b;border-color:#e93329;}
    .event-miro-error {background:#ffa500;border-color:#e93329;}
</style> 

<apex:sectionHeader title="Miro's Calendar"/>
<apex:outputPanel id="calPanel">
    <apex:form >
        <!--<div id="cal-options">
            <apex:commandButton value="{!IF(includeMyEvents,'Hide My Events','Show My Events')}" action="{!toggleMyEvents}"/>
        </div>-->
        <div id="cal-legend">
            <ul>
                <li><span class="event-miro-corp"></span>Corp Installs</li>
                <li><span class="event-miro-bank"></span>Bank Installs</li>
                <li><span class="event-miro-retail"></span>Retail Installs</li>
                <li><span class="event-miro-other"></span>Other</li>
                <!--<li><span class="event-miro-error"></span>Error</li>-->
            </ul>
            <div style="clear:both;"><!--fix floats--></div>
        </div>
        <div style="clear:both;"><!--fix floats--></div>
        <div id="calendar"></div>
    </apex:form>
</apex:outputPanel>

Controller:

public class MiroCalendar_Controller02 {
public list<calEvent> events {get;set;}   

//The calendar plugin is expecting dates is a certain format. We can use this string to get it formatted correctly
String dtFormat = 'EEE, d MMM yyyy HH:mm:ss z';
//constructor

public PageReference pageLoad() {
    events = new list<calEvent>();

    //Get my Events if we have selected the correct option
    for(Event evnt: [select Id, Subject, isAllDayEvent, Description, StartDateTime, EndDateTime, CreatedById, CreatedDate, LastModifiedById, LastModifiedDate from Event where OwnerID = :'005i0000000jxVsAAI' AND ActivityDate > LAST_MONTH]){
        DateTime startDT = evnt.StartDateTime;
        DateTime endDT = evnt.EndDateTime;
        DateTime createdOn = evnt.CreatedDate;
        DateTime modifiedOn = evnt.LastModifiedDate;
        String createdByName = '';
        String modifiedByName = '';
        String classType = '';
        calEvent miroEvent = new calEvent();                

        classType = 'event-miro-corp';

        miroEvent.title = evnt.Subject;
        miroEvent.allDay = evnt.isAllDayEvent;
        miroEvent.startString = startDT.format(dtFormat);
        miroEvent.endString = endDT.format(dtFormat);
        miroEvent.url = '/' + evnt.Id;
        miroEvent.className = classType;
        miroEvent.description = evnt.Description;
        miroEvent.createdBy = createdByName;
        miroEvent.createdOnString = createdOn.format(dtFormat);
        miroEvent.modifiedBy = modifiedByName;
        miroEvent.modifiedOnString = modifiedOn.format(dtFormat);
        events.add(miroEvent);
        }
    return null;
}    


//Class to hold calendar event data
public class calEvent{
    public String title {get;set;}
    public Boolean allDay {get;set;}
    public String startString {get;private set;}
    public String endString {get;private set;}
    public String url {get;set;}
    public String className {get;set;}
    public String description {get;set;}
    public String createdBy {get;set;}
    public String createdOnString {get;set;}
    public String modifiedBy {get;set;}
    public String modifiedOnString {get;set;}
}
}

TEST Controller:

@isTest
public class MiroCalendar_Controller02Tests {

public static testMethod void testMiroController02() {
    PageReference pageRef = Page.MiroCalendar;
    Test.setCurrentPage(pageRef);

    // Instantiate a new controller with all parameters in the page
    MiroCalendar_Controller02 testcontroller = new MiroCalendar_Controller02();
    testcontroller.pageLoad();

}
}

2 Answers 2

2

Unit tests are meant to exercise your code so that you can test what you would expect to happen in your code. When I'm writing unit tests, I go back to the functional requirements of my code and think about what logic it performs. How would I setup all of the context and data for my code to execute in the way I was expecting?

In your case, you are returning a list of events. As such, your unit test needs to create those events so they can be returned. The reason that you have big blocks of code not being exercised is because you aren't getting into the for loop of your test because there are no events in the database in the unit test context (isolated from the data in your instance). Create an event, which you'll need to hardcode to the specific owner you listed in your controller prior to all of the code that's currently in your test.

Here is a good article about best practices for writing unit tests: https://developer.salesforce.com/page/How_to_Write_Good_Unit_Tests

7
  • How do I "pass" the isolated event to the for loop in the pageLoad()? Or do I simply build it in the test BEFORE calling the pageLoad()? (I haven't read that article before though I've read a lot from the Salesforce dev articles... I'll read it over now!)
    – Amanda
    Commented Jun 18, 2014 at 14:29
  • To further add, nested classes are not covered when writting test code within salesforce, so you'll just have to create an instance of calEvent to cover it and it's associated fields. Lastly, use either Eclipse or Developer Console to see what lines of code are not covered when writing your test methods.
    – Double A
    Commented Jun 18, 2014 at 14:30
  • Lastly, and this is a personal preference, I like to create my own test data within the test method to avoid any issues when deploying to a new instance, I believe by default @isTest sets SeeAllData to true I always set it to false, so it looks like such @isTest(SeeAllData=false) this way I dont have any issues if something is unique.
    – Double A
    Commented Jun 18, 2014 at 14:33
  • 1
    Correct, just create at least one event prior to instantiating your controller and calling pageLoad()
    – greenstork
    Commented Jun 18, 2014 at 14:36
  • Since the controller actually creates the sub-class, I think it will get covered just by creating an event.
    – greenstork
    Commented Jun 18, 2014 at 14:37
2

The following line is what you aren't taking into account:

for(Event evnt: [select Id, Subject, isAllDayEvent, Description, StartDateTime, EndDateTime, CreatedById, CreatedDate, LastModifiedById, LastModifiedDate from Event where OwnerID = :'005i0000000jxVsAAI' AND ActivityDate > LAST_MONTH])

You are querying for data, but your test doesn't create any of this data. This is why you aren't getting any test coverage.

Before you call your controller, in your test class will need something similar to:

//You will need to adjust this to your purposes.  This part is just to get you started.
insert new Event(OwnerId = '005i0000000jxVsAAI', ActivityDate = ??, etc);

Also, as an additional note, it's a bad idea to hardcode Ids like you are doing for your OwnerId.

2
  • Thank-you, noted - but this is the only user that we will ever want this for. I do realize that different ORGs will not be able to use the controller based on that hard coding, but, we have 1000s of events and we need only this specific user. I don't see a way to do this differently (but I'm open to suggestions of course!). All other users schedule things for this user based on his availability. Thank you for the testing suggestion above as I was just refreshing on how to insert "new" anything in to the test. Much appreciated.
    – Amanda
    Commented Jun 18, 2014 at 14:58
  • You can query for the User through whatever criteria you have for them. Alternatively, you could create a custom setting for it. I just suggest never hardcoding IDs since they change between orgs.
    – dphil
    Commented Jun 18, 2014 at 15:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .