0

I am trying to deploy a change-set which contains 12 new fields from my development environment to my production environment. These are all simple fields, 7 for the account screen and 5 for a custom object called Credit History. I get the error:

DefineOppMPANController.testAdd(); DefineOppMPANController.testCancel(); null, Details: System.QueryException: List has no rows for assignment to SObject Class.DefineOppMPANController.<init>: line 12, column 1 Class.DefineOppMPANController.testAdd: line 102, column 1; System.QueryException: List has no rows for assignment to SObject Class.DefineOppMPANController.<init>: line 12, column 1 Class.DefineOppMPANController.testCancel: line 93, column 1; Average test coverage across all Apex Classes and Triggers is 3%, at least 75% test coverage is required.

I don't understand why it won't let me deploy the fields, as they are just fields. There's no apex code or anything, literally just empty string and number fields. What can I do to get these fields copied over without having to make them manually?

Edit: This is the only test that's running, and the only one that exists.

**
 * Determine valid list of potential mpans given an opportunities account(s)
 *
 * Kent Matheson - EDW Technology 20/03/2009
 */
public class DefineOppMPANController {

    public Opportunity o { get; set; }
    public List<DTOOppMpanSelection> dtos { get; set; } 

    public DefineOppMPANController () {
      o = [select id, accountid, name from Opportunity where id = :ApexPages.currentpage().getParameters().get('id')];
      dtos = setUpFormData();        
    }

    private List<DTOOppMpanSelection> setUpFormData(){
      List<DTOOppMpanSelection> dtos = new List<DTOOppMpanSelection>();
      List<ID> clan = getClan(o.accountid);
      List<Site__C> sites = [SELECT s.id, s.name FROM Site__c s WHERE s.account__c IN :clan];
      List<MPAN__c> mpans = [SELECT m.id, m.name, m.mpan_descriptor__c FROM MPAN__c m WHERE m.site_number__C IN :sites ORDER BY m.mpan_descriptor__c];
      List<Opportunity_MPAN__c> existingMpans = [SELECT om.MPAN__r.id FROM Opportunity_MPAN__c om WHERE om.opportunity__c = :o.id];
      Map<ID, Opportunity_MPAN__c> existingMpanIds = new Map<ID,Opportunity_MPAN__c>();
      for(Opportunity_MPAN__c oppmpan: existingMpans){
        existingMpanIds.put(oppmpan.MPAN__r.id,
                            oppmpan);
      }
      for(MPAN__c mpan: mpans){
        if(existingMpanIds.containsKey(mpan.id)){
          dtos.add(new DTOOppMpanSelection(o.id, mpan.id, true, mpan.name, mpan.mpan_descriptor__c));
        } else{
          dtos.add(new DTOOppMpanSelection(o.id, mpan.id, false, mpan.name, mpan.mpan_descriptor__c));
        }
      }
      return dtos;
    }

    private List<ID> getClan(ID currentId){
      List<ID> clan = new List<ID>{};
      List<Account> al = new List<Account>{};
      List<ID> currentParents = new List<ID>{currentId};
      Integer level = 0;
      Boolean endOfStructure = false;
      // Loop though all kids
      while (!endOfStructure ){
        if(level==0){
          al = [SELECT a.Id FROM Account a WHERE a.id IN :currentParents];
        } else {
          al = [SELECT a.Id FROM Account a WHERE a.ParentID IN :currentParents];
        }
        if(al.size() == 0){
          endOfStructure = true;
        }else {
          currentParents.clear();
          for (Integer i = 0 ; i < al.size(); i++){
            Account a = al[i];
            currentParents.add(a.id);
            clan.add(a.id);
          }
          level++;
        }
      } 
      return clan;  
    }


    public pageReference save() {
      Opportunity_MPAN__c[] to_add = new Opportunity_MPAN__c[] {};
      List<Opportunity_MPAN__c> existingMpans = [SELECT om.id FROM Opportunity_MPAN__c om WHERE om.opportunity__c = :o.id];
      delete existingMpans;
      for(DTOOppMpanSelection dto: dtos){
        if(dto.selected){
          to_add.add(new Opportunity_MPAN__c( Opportunity__c = dto.oppId, Mpan__c = dto.mpanId));
        }
      }
      insert to_add;

      pageReference pr = new pageReference('/'+ o.id);
      pr.setredirect(true);
      return pr;  
    }

    public pageReference cancel() {
      pageReference pr = new pageReference('/'+ o.id);
      pr.setredirect(true);
      return pr;  
    }

    public static testMethod void testCancel() {
      pageReference p = Page.defineOppMpans;
      p.getParameters().put('id', '006200000086Zqw' );

      test.setCurrentPage( p );
      DefineOppMPANController defOMC = new DefineOppMPANController();
      defOMC.cancel();
    }

    public static testMethod void testAdd() {
      pageReference p = Page.defineOppMpans;
      p.getParameters().put('id', '006200000086Zqw' );

      test.setCurrentPage( p );
      DefineOppMPANController defOMC = new DefineOppMPANController();
      defOMC.save();
    }
}

4 Answers 4

3

Your problem is here:

o = [select id, accountid, name from Opportunity where id = :ApexPages.currentpage().getParameters().get('id')];

This code throws exception (the one you're seeing) if no results are returned by the query.

The reason you have no results is that the code is using a hardcoded ID:

public static testMethod void testAdd() {
  pageReference p = Page.defineOppMpans;
  p.getParameters().put('id', '006200000086Zqw' );

  test.setCurrentPage( p );
  DefineOppMPANController defOMC = new DefineOppMPANController();
  defOMC.save();
}

This record might exist in the Sandbox, but the chances of that ID being valid in production are all but zero, and developers should NEVER hardcode IDs.

This test method is correctly setting the page parameter etc. but first it needs to create and insert an opportunity as part of the test, and then use the ID of that record as the page parameter.

Tests are run when deploying fields because that's what they're there for. You could easily have code doing some describes or other shenanigans that gets broken by a new field with a type or similar that it's not anticipating. Tests are there to protect your org from regressions, and while they can be a complete and utter pain in the ass they should be embraced.

1

To @kazoolsky's comment, if your ORG is small enough that creating new fields in Prod will not impact the business than I would advise doing that. Do you have a development team/person? If not you can hire SF developers on a contract basis to do small jobs like this. Test code often needs to be updated as the ORG grows/changes. Hope that helps, good luck!

*Also there is great info on https://developer.salesforce.com/ go to Trailhead and try their intro to dev. classes, they are really helpful and easy to follow.

0

Seems like your test-code coverage is not sufficient to deploy anything. A proper solution is to fix that. A quick and dirty solution is to recreate fields manually in production

5
  • I don't understand why the APEX tests are even running, I'm not importing anything to do with APEX. Can I turn these tests off?
    – Simkill
    Jul 29, 2015 at 13:24
  • Nope. Well you can, technically, but its just easier to recreate fields in production straightaway and get down to fixing the code-coverage while not being in a hurry anymore
    – kazoolsky
    Jul 29, 2015 at 13:26
  • I would rather fix the code coverage first. We have a lot of work coming up and getting that done sooner is going to be more beneficial. I don't even know APEX, I don't know how to fix it, or where to fix it, or even what 'code coverage' means. I'm standing in for the application manager who is currently on maternity leave.
    – Simkill
    Jul 29, 2015 at 13:28
  • 2
    Well. Time for research then! salesforce.stackexchange.com/questions/25892/… help.salesforce.com/apex/…
    – kazoolsky
    Jul 29, 2015 at 13:30
  • If you find my comments useful - feel free to mark the answer as "acceptable" =)
    – kazoolsky
    Jul 29, 2015 at 13:35
0

Extracting from a official document for you.

For deployment to a production organization, all local tests in your organization are run by default. Tests that originate from installed managed packages aren’t run by default. If any test fails, the entire deployment is rolled back.

If the deployment includes components for the following metadata types, all local tests are run. For change sets, this test execution behavior applies to all API versions.

  • ApexClass
  • ApexComponent
  • ApexPage
  • ApexTrigger
  • ArticleType
  • BaseSharingRule
  • CriteriaBasedSharingRule
  • CustomDataType
  • CustomField
  • CustomObject
  • DataCategoryGroup
  • Flow
  • InstalledPackage
  • NamedFilter
  • OwnerSharingRule
  • PermissionSet
  • Profile
  • Queue
  • RecordType
  • RemoteSiteSetting
  • Role
  • SharingReason
  • Territory
  • Validation Rules
  • Workflow

So, you can not control the test running behavior when it is production. To resolve the issue you must cover the code to acceptable percentage 75% in production.

2
  • Can I not just delete the tests? These two tests were written back in 2009 by our software provider and appear to have nothing to do with inbound change sets.
    – Simkill
    Jul 29, 2015 at 13:42
  • It has nothing to do with the change set, it has to do with the Org itself. Basically whenever a change is deployed to the org, tests run to make sure you're not breaking the code, if the tests aren't well written or the org changes sufficiently to invalidate their test data they will fail, deleting them will reduce your code coverage so that's not an option. The best approach here is to update the tests, if you can't do that you're going to have to create the fields directly in production.
    – thegogz
    Jul 29, 2015 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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