Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

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();

}
}
share|improve this question
add comment

4 Answers 4

up vote 1 down vote accepted

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

share|improve this answer
    
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 Jun 18 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 Jun 18 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 Jun 18 at 14:33
1  
Correct, just create at least one event prior to instantiating your controller and calling pageLoad() –  greenstork Jun 18 at 14:36
    
Since the controller actually creates the sub-class, I think it will get covered just by creating an event. –  greenstork Jun 18 at 14:37
show 1 more comment

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.

share|improve this answer
    
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 Jun 18 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 Jun 18 at 15:04
add comment

Your query has hardcoded value and thats why writing a better test code wont be possible .

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

The above query returns no rows .

Correct below

1)Don't hardcode ID as it may change from environment to environment

2) Create even record in test class with activity date >Last month

Event e=new Event();
e.subject='test';
e.description='test';
e.starttime=now();
e.endtime=now()+1;
insert e;

3)Assert your test code behaviour

share|improve this answer
    
While I think it is a bad practice to hardcode an Id, it's technically possible to test this code as is. –  greenstork Jun 18 at 14:52
    
I recognize that hard-coding that OwnerId is bad practice, and will not transfer between ORGs, but how else would I eliminate other owners? Or Isolate only events from that owner? I will continue to test as is, but, as I stated earlier, I'm open to correcting the hard-coding issue... I just don't see a way to do it... My comments above also relate to this issue and how the calendar is used. –  Amanda Jun 18 at 15:01
    
@greenstork Its possible but not good approach . –  Mohith Shrivastava Jun 18 at 15:32
    
@Amanda in your test code you will need to query for that user with Id as 005i0000000jxVsAAI and then use system.RunAs method –  Mohith Shrivastava Jun 18 at 15:34
    
Rather than hardcode the Id in the class, define a Custom Setting called 'Calendar User', of type List, with a checkbox called 'Active' and String called 'Id'. Then create a Calendar User record with the user's name ('Joe U$er'), Active=true and their Id. –  Duncan Stewart Jun 18 at 18:26
show 1 more comment

Thanks Everyone! With the suggestions and guidance you provided I was able to Test to 100% coverage. I really appreciate your help and I will continue to work on the best practices issue within the code.

Thanks again!

share|improve this answer
    
Please upvote all the helpful answers and mark the best as the answer so that other with this problem can benefit. T –  Richard N Jun 18 at 20:38
    
Please add as comment .Not as answer . –  Mohith Shrivastava Jun 20 at 11:07
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.