+ Start a Discussion
slaneslane 

Packaging and deployment woes

Hello all:

 

So my question concerns a complicated situation. I'm new enough to packaging that I'm not at all sure I'm going to state the problem accurately but I'll try.

 

I'm part of a team working on a Force.com app. The org in which we're working has a development sandbox, a test/QA environment, and a production environment. We're using the Force.com Eclipse IDE for pretty much everything, including migration from dev to test.

 

A few weeks ago we tried to push to production. We encountered an error that prevented our tests from running, and we're following up on that issue with SFDC so that's a separate converstion.

 

Here's the nub. To get our app deployed it was recommended we instead upload our package to production via the web interface. This seems to have worked, net the usual tinkering. We uploaded as an unmanaged package.(Side note: am I right in thinking this involved somehow funneling the app through the AppExchange?)

 

The problem is that now we can no longer deploy using our original package, the one recognized by the IDE and the one we use to keep our project definition together. What we've now been told by those who specialize in deployment in this org is that we can only deploy to production using Ant, and sending the deployer a listof all the changed components so that an Ant script can be pulled together that just updates those components.

 

I can tell there are things I still just don't "get" about packages and migration, and I know the above is a lot to wade through, but I'm curious whether any can tell me what was different about uploading the package via the web UI, and why our Eclipse IDE deployments apparently no longer work.

 

Any insight greatfully received.

 

-- Steve Lane

rwoollenrwoollen

First off, merely uploading a package doesn't make it available on the AppExchange.  After you upload a package, you can optionally register it on the AppExchange, but that's a separate step.  If you don't register it on the AppExchange, then the package is still installable via the installation URL that you see after upload (or when you click on the version link in your development organization).

 

The eclipse IDE plug-in (and the similar ant tasks) are written on top of our metadata API.  Essentially the retrieve extracts metadata from your organization into a set of XML files.  The deploy operation does the reverse and creates/updates metadata in your organization from the XML files.

 

Typically you'd use the eclipse plug-in / ant tasks when you're moving metadata between different organizations (or source control) within the same company.  For instance when multiple developers in separate salesforce organizations are collaborating on a project.

 

Packaging is more intended for ISVs developing applications for their customers to install.

 

That being said, unmanaged packages blur the lines since they pre-date the metadata API and are often used for moving metadata within the same company.

 

Hope that helps.  Can you give some more details on the error you're seeing using the eclipse IDE?

slaneslane

Thanks. The more I learn about packaging, the more it seems a poor fit for what we need to do. We're developing in a large, complex org. We have two sandboxes, one for development and one for testing, and of course we have production.

 

Initially we used what I'll call a "development" package (is there a better name for this?) solely as a means to define our Eclipse project. New developers setting up the project just pull all components from package X.

 

When we tried to deploy to production we ran into an issue (escalated to SFDC already) wherein the code base was so large that the "run all tests" task timed out on the client side. It was advised we deploy via an unmanaged package to get around this, which we did.

 

The unmanaged package appears not to be updatable. If we try to add a field to a custom object in the package, then redeploy that custom object via Eclipse, we get the message that we can't update an installed package. Oddly, deploying just the one added field via Ant did work.

 

My instinct is we should ditch packaging as a project definition tool. It seems easiest to keep the project checked into source control and have the repository be the project definition.

 

Still puzzled why Eclipse can't update items in our installed package and it seems Ant can.

khanWebgurukhanWebguru

I have problem regarding Package deployment in sales-force :(  Its very strange that I have just 4 triggers and have not any single Apex Class so what I have to do for deployment I need TEST Methods for my Triggers. I created test methods for my all four triggers when I am going to upload option of package I am getting following ERROR :(

 

Component                          
ProblemApex Class Average test coverage across all Apex Classes and Triggers is 1%, at least 75% test coverage is required

 

Thats very strange because I have not Apex class even that I am getting this problem please help me in this problem :(

 

My Triggers are mentioned below:

 

 

Trigger 1:

 

 

trigger UpdateHiringManagerFieldAfterInsert on Job_Application__c (after insert) 
{
    LIST<Job_Application__c> objJobApp = [select Position__c From Job_Application__c where Id =  :Trigger.new[0].Id limit 1];
    LIST<Position__c> objPos = [select Hiring_Manager__c,Name From Position__c where ID =  :objJobApp[0].Position__c limit 1];
    objJobApp[0].HiringManager__c = objPos[0].Hiring_Manager__c;
    update objJobApp[0];

 

Trigger 2:

 

trigger insertPosition on Recruitment_Request__c (after update) 
{
    if(Trigger.new[0].Approved__c != Trigger.old[0].Approved__c && Trigger.new[0].Approved__c == True)
    {
        Recruitment_Request__c rrc = [select PositionId__c From Recruitment_Request__c where Id =  :Trigger.new[0].Id limit 1];
        if(rrc.PositionId__c == '' || rrc.PositionId__c == Null)
        {
            Position__c position = new Position__c();
            position.Name = Trigger.new[0].Name;
            position.Hiring_Manager__c = Trigger.new[0].CreatedById;
            //position.Min_Pay__c = 5000;
            //position.Min_Pay__c = 10000;
            insert position;
            rrc.PositionId__c = position.Id;
            update rrc;
        }
    }
}

 

Trigger 3:

 

trigger onInsertAndUpdateInterview on Interview__c (after insert, after update) 
{
    if(Trigger.new[0].JobApplicationId__c == '' || Trigger.new[0].JobApplicationId__c == Null)    
    {
        LIST<Interview__c> objInterview = [select JobApplicationId__c,OwnerUser__c From Interview__c where Candidate__c =  :Trigger.new[0].Candidate__c AND Job_Position__c =  :Trigger.new[0].Job_Position__c AND JobApplicationId__c != ''  limit 1];
        
        LIST<Interview__c> objInterview1 = [select JobApplicationId__c,OwnerUser__c From Interview__c where ID =  :Trigger.new[0].ID  limit 1];
        
        objInterview1[0].JobApplicationId__c = objInterview[0].JobApplicationId__c;
        objInterview1[0].Job_Application__c = objInterview[0].Job_Application__c;
        objInterview1[0].OwnerUser__c = objInterview[0].OwnerUser__c;
        objInterview1[0].OwnerId=objInterview[0].OwnerUser__c;
        
        
        update objInterview1[0];
        
        
    }
    if(Trigger.new[0].Create_Another_Interview__c == True && Trigger.new[0].InterviewType__c == 'Technical')
    {     
        
        Interview__c interview = new Interview__c();
        interview.Candidate__c = Trigger.new[0].Candidate__c;
        interview.Job_Position__c = Trigger.new[0].Job_Position__c;
        interview.JobApplicationId__c = Trigger.new[0].JobApplicationId__c;
        interview.Job_Application__c = Trigger.new[0].Job_Application__c;
        interview.OwnerUser__c = Trigger.new[0].OwnerUser__c;
        interview.InterviewType__c = Trigger.new[0].InterviewType__c;
        interview.OwnerId = Trigger.new[0].OwnerUser__c;
        interview.HiringManager__c = Trigger.new[0].HiringManager__c;
        interview.IsCreatedByCloneOption__c = True;
        interview.Date__c = date.today();
        insert interview;
        
        LIST<Interview__c> objInterview1 = [select JobApplicationId__c,OwnerUser__c From Interview__c where ID =  :Trigger.new[0].ID  limit 1];
        objInterview1[0].Create_Another_Interview__c  = false;
        update objInterview1[0];
        
        
        
        
        
        
        
    }
    if(Trigger.new[0].Create_Another_Interview__c == True && Trigger.new[0].InterviewType__c == 'HR')
    {     
        
        Interview__c interview = new Interview__c();
        interview.Candidate__c = Trigger.new[0].Candidate__c;
        interview.Job_Position__c = Trigger.new[0].Job_Position__c;
        interview.JobApplicationId__c = Trigger.new[0].JobApplicationId__c;
        interview.OwnerUser__c = Trigger.new[0].OwnerUser__c;
        interview.InterviewType__c = Trigger.new[0].InterviewType__c;
        interview.OwnerId = Trigger.new[0].OwnerUser__c;
        interview.HiringManager__c = Trigger.new[0].HiringManager__c;
        interview.IsCreatedByCloneOption__c = True;
        interview.Date__c = date.today();
        insert interview;
        
        LIST<Interview__c> objInterview1 = [select JobApplicationId__c,OwnerUser__c From Interview__c where ID =  :Trigger.new[0].ID  limit 1];
        objInterview1[0].Create_Another_Interview__c  = false;
        update objInterview1[0];
        
        
        
        
        
        
        
    }
}

 

Trigger 4:

 

trigger sendApprovalRequestToManager on Job_Application__c (after update) 
{
   
    if(Trigger.new[0].Picklist__c != Trigger.old[0].Picklist__c && Trigger.new[0].Picklist__c == 'Short Listed')
    {
        
  
        LIST<Job_Application__c> objJobApp1 = [select Position__c,CreatedById From Job_Application__c where Id =  :Trigger.new[0].Id limit 1];
        
        // Inserting record in Interview so automatically an interview create with basic Info
        Interview__c interview = new Interview__c();
        interview.Candidate__c = Trigger.new[0].Candidate__c;
        interview.Job_Position__c = Trigger.new[0].Position__c;
        interview.JobApplicationId__c = Trigger.new[0].Id;
        interview.Job_Application__c = Trigger.new[0].Id;
        interview.InterviewType__c = 'Technical';
        interview.OwnerUser__c = objJobApp1[0].CreatedById;
        interview.HiringManager__c = Trigger.new[0].HiringManager__c;
        interview.OwnerId=objJobApp1[0].CreatedById;        
        interview.Date__c = date.today();
        
        
        
        
        
        
     
    }
    if(Trigger.new[0].Picklist__c != Trigger.old[0].Picklist__c && Trigger.new[0].Picklist__c == 'Recommended')
    {
                
   
        LIST<Job_Application__c> objJobApp2 = [select Position__c,CreatedById From Job_Application__c where Id =  :Trigger.new[0].Id limit 1];
        
        // Inserting record in Interview so automatically an interview create with basic Info
        Interview__c interview1 = new Interview__c();
        interview1.Candidate__c = Trigger.new[0].Candidate__c;
        interview1.Job_Position__c = Trigger.new[0].Position__c;
        interview1.JobApplicationId__c = Trigger.new[0].Id;
        interview1.Job_Application__c = Trigger.new[0].Id;
        interview1.InterviewType__c = 'HR';
        interview1.OwnerUser__c = objJobApp2[0].CreatedById;
        interview1.HiringManager__c = Trigger.new[0].HiringManager__c;
        interview1.OwnerId=objJobApp2[0].CreatedById;
        interview1.Date__c = date.today();
        
        
        //  After inserting interview object also have ID of that record that will be use in next
        insert interview1;
        
       
        
    }
    


}

 

Please see reply for test class I did n reply because of character limit :(

 

 

Regards,

 

Asif Khan

 

khanWebgurukhanWebguru

 

 

Now I created these test Cases against above mentioned Triggers :(

 

public class PalmRecruitingTesting
{
    static testMethod void UpdateHiringManagerFieldAfterInsert()
    {                           
       Job_Application__c obj = new Job_Application__c();        
        LIST<Job_Application__c> objJobApp = [SELECT HiringManager__c, Position__c,Candidate__c FROM Job_Application__c limit 1];        
        LIST<Position__c> objPos = [select Hiring_Manager__c,Name From Position__c where ID =  :objJobApp[0].Position__c limit 1];
        objJobApp[0].HiringManager__c = objPos[0].Hiring_Manager__c;
        obj.HiringManager__c = objJobApp[0].HiringManager__c;
        obj.Position__c = objJobApp[0].Position__c;
        obj.Candidate__c = objJobApp[0].Candidate__c;
        
        test.startTest();                
        insert obj;
           
        
        obj.Cover_Letter__c = 'Test';
        
        
        update obj;
        System.assertEquals(obj.Cover_Letter__c,'Test');
         LIST<Job_Application__c> objJobApp1 = [SELECT Cover_Letter__c,HiringManager__c, Position__c,Candidate__c FROM Job_Application__c];
       Integer j=0;
        for (Job_Application__c objJAP :objJobApp1) 
        {
        
            ++j;
            objJAP.Cover_Letter__c = string.valueof(j);            
            System.assertEquals(objJAP.Cover_Letter__c,string.valueof(j));
            
        
        }
        update objJobApp1;
        
        test.stopTest();
        
        
    }

    static testMethod void onInsertAndUpdateInterview()
    {                                   
        Interview__c c = [SELECT Id FROM Interview__c limit 1];        
        
        test.startTest();
        update c;
        
        List<Interview__c> c1 = [SELECT Id, Final_Comments__c FROM Interview__c];
        Integer j=0;
        for (Interview__c c11 :c1) 
        {
            ++j;
            c11.Final_Comments__c = string.valueof(j);                        
            System.assertEquals(c11.Final_Comments__c,string.valueof(j));
            
        
        }
        update c1;
        test.stopTest();   
    }
    
    static testMethod void insertPosition()
    {                                   
        Recruitment_Request__c c = [SELECT Id FROM Recruitment_Request__c limit 1];        
        
        test.startTest();
        update c;
        
        
        List<Recruitment_Request__c> c1 = [SELECT Id,Comments__c FROM Recruitment_Request__c];
        Integer j=0;
        for (Recruitment_Request__c c11 :c1) 
        {
            ++j;
            c11.Comments__c = string.valueof(j);                        
            System.assertEquals(c11.Comments__c,string.valueof(j));
            
        
        }
        update c1;
        test.stopTest();   
    }
    
    static testMethod void sendApprovalRequestToManager()
    {                                   
        //Interview__c c = [SELECT Id FROM Interview__c limit 1];        
        
        LIST<Position__c> objPos1 = [select Id, Name From Position__c limit 1];

       LIST<Candidate__c> objCand1 = [select Id, First_Name__c,Last_Name__c From Candidate__c limit 1];
       
       LIST<Job_Application__c> objJobApp2 = [select Id, Position__c,CreatedById,HiringManager__c From Job_Application__c limit 1];
       
       
        Interview__c interview1 = new Interview__c();
        interview1.Candidate__c = objCand1[0].Id;
        interview1.Job_Position__c = objPos1[0].Id;
        interview1.JobApplicationId__c = objJobApp2[0].Id;
        interview1.Job_Application__c = objJobApp2[0].Id;
        interview1.InterviewType__c = 'HR';
        interview1.OwnerUser__c = objJobApp2[0].CreatedById;
        interview1.HiringManager__c = objJobApp2[0].HiringManager__c;
        interview1.OwnerId=objJobApp2[0].CreatedById;
        interview1.Date__c = date.today();
        
        test.startTest();
        insert interview1;
        
        
        
        List<Interview__c> c1 = [SELECT Id, Final_Comments__c FROM Interview__c];
        Integer j=0;
        for (Interview__c c11 :c1) 
        {
            ++j;
            c11.Final_Comments__c = string.valueof(j);                        
            System.assertEquals(c11.Final_Comments__c,string.valueof(j));
            
        
        }
        update c1;
        test.stopTest();    
    }

}

 

 

But on uploading of package I am getting this error

 


Average test coverage across all Apex Classes and Triggers is 1%, at least 75% test coverage is required

 

Please help me in this

 

Thanks,

 

Asif Khan