function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Daniel B ProbertDaniel B Probert 

Apex Class test coverage issues.

Hi All,

 

I'm having an issue getting my code coverage for an Apex class i've written above 0.

 

here is the apex class:

 

public class TwilioMMASyncSMS {
	@future (callout=true)
		public static void sendSMS(Set<Id> Ids) {
    		String toNumber = '';
    		String messageBody = '';
    	for (Monitoring_Visit_Bursary__c mvp: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit_Bursary__c WHERE id IN :Ids]) {
            if (mvp.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientmvp = TwilioAPI.getDefaultClient();
            	toNumber = mvp.User_ID_Phone__c;
            	messageBody = mvp.SMSBody__c;
            	Map<String,String> params = new Map<String,String> {
                	'To'   => toNumber,
                	'From' => '+14156830489',
                	'Body' => messageBody
             	};
         	TwilioSMS sms = SMSclientmvp.getAccount().getSmsMessages().create(params);
			}
    	}
     	for (School_Termly_Update__c stu: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM School_Termly_Update__c WHERE id IN :Ids]) {
            if (stu.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientstu = TwilioAPI.getDefaultClient();
            	toNumber = stu.User_ID_Phone__c;
            	messageBody = stu.SMSBody__c;
             	Map<String,String> params = new Map<String,String> {
                	'To'   => toNumber,
               		'From' => '+14156830489',
                 	'Body' => messageBody
              	};                 
        	TwilioSMS sms = SMSclientstu.getAccount().getSmsMessages().create(params);
            }
     	}
     	for (Structure_Status_Report__c ssr: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Structure_Status_Report__c WHERE id IN :Ids]) {
            if (ssr.User_ID_Phone__c != null) {
         	TwilioRestClient SMSclientssr = TwilioAPI.getDefaultClient();
             	toNumber = ssr.User_ID_Phone__c;
             	messageBody = ssr.SMSBody__c;
              	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                 	'From' => '+14156830489',
                  	'Body' => messageBody
              	};
        	TwilioSMS sms = SMSclientssr.getAccount().getSmsMessages().create(params);
            }
     	}
         for (New_Cama_Review__c ncr: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM New_Cama_Review__c WHERE id IN :Ids]) {
            if (ncr.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientncr = TwilioAPI.getDefaultClient();
              	toNumber = ncr.User_ID_Phone__c;
              	messageBody = ncr.SMSBody__c;
              	Map<String,String> params = new Map<String,String> {
                  	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
               	};
        	TwilioSMS sms = SMSclientncr.getAccount().getSmsMessages().create(params);
           	}
         }   		
         for (Background_data__c bd: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Background_data__c WHERE id IN :Ids]) {
            if (bd.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientbd = TwilioAPI.getDefaultClient();
             	toNumber = bd.User_ID_Phone__c;
              	messageBody = bd.SMSBody__c;
               	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
               	};
        	TwilioSMS sms = SMSclientbd.getAccount().getSmsMessages().create(params);
            }
         } 
         for (New_Grade_10_Girls__c ng10g: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM New_Grade_10_Girls__c WHERE id IN :Ids]) {
            if (ng10g.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientng10g = TwilioAPI.getDefaultClient();
             	toNumber = ng10g.User_ID_Phone__c;
             	messageBody = ng10g.SMSBody__c;
             	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
              	};           
        	TwilioSMS sms = SMSclientng10g.getAccount().getSmsMessages().create(params);
            }
         }
         for (Monitoring_Visit__c mv: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit__c WHERE id IN :Ids]) {
            if (mv.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientmv = TwilioAPI.getDefaultClient();
              	toNumber = mv.User_ID_Phone__c;
             	messageBody = mv.SMSBody__c;
              	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
              	};
       	 	TwilioSMS sms = SMSclientmv.getAccount().getSmsMessages().create(params);
           	}
         }  
         for (Camfed_Annual_data__c cad: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Camfed_Annual_data__c WHERE id IN :Ids]) {
            if (cad.User_ID_Phone__c != null) {
            TwilioRestClient SMSclientcad = TwilioAPI.getDefaultClient();
             	toNumber = cad.User_ID_Phone__c;
             	messageBody = cad.SMSBody__c;
            	Map<String,String> params = new Map<String,String> {
                   	'To'   => toNumber,
                   	'From' => '+14156830489',
                   	'Body' => messageBody
              	};
        	TwilioSMS sms = SMSclientcad.getAccount().getSmsMessages().create(params);
            }  	  			  		
     	}
	}
}

 When writing the test class I figured that I would need to create an example of all the related object records that allow me to get this information (note that SMSBody and User_ID_Phone__c are formula fields).

 

This is the test class that I have written:

 

@isTest
public class TwilioMMASyncSMSTest {
			static testMethod void TwilioMMASyncSMS() {
				String toNumber = '';
    			String messageBody = '';
					// create a country
						Country__c country = new Country__c();
						country.Name = 'test country';
						insert country;
					// create a district
						District__c district = new District__c(); 
						district.Name = 'test';
						district.Country__c = country.id;
						insert district;
					// create a school
						School__c school = new School__c();
						school.Name = 'test';
						school.District__c = district.id;
						insert school;
					// create a contact
						Contact contact = new Contact();
						contact.LastName = 'test';
						contact.District__c = district.id;
						contact.School__c = school.id;
						contact.Country__c = country.id;
						insert contact;
					//create a structure
						Structure__c structure = new Structure__c();
						structure.Country__c = country.id;
						structure.District__c = district.id;
						structure.Chair__c = contact.id;
						structure.School__c = school.id;
						insert structure;
					// create a Monitoring Visit to Bursary
						Monitoring_Visit_Bursary__c mvp = new Monitoring_Visit_Bursary__c();
						mvp.Person__c = contact.id;
						mvp.User_ID__c = 'uk07717417554@gmail.com';
						insert mvp;
					// create school termly update
						School_Termly_Update__c stu = new School_Termly_Update__c();
						stu.School__c = school.id;
						stu.User_ID__c = 'uk07717417554@gmail.com';
						insert stu;
					// create Structure Status Report
						Structure_Status_Report__c ssr = new Structure_Status_Report__c();
						ssr.Structure__c = structure.id;
						ssr.User_ID__c = 'uk07717417554@gmail.com';
						insert ssr;
					// create New Cama Review
						New_Cama_Review__c ncr = new New_Cama_Review__c();
						ncr.Country__c = country.id;
						ncr.District__c = district.id;
						ncr.Graduation_School_Same_District__c = school.id;
						ncr.User_ID__c = 'uk07717417554@gmail.com';
						insert ncr;
					// create Background Data
						Background_data__c bd = New Background_data__c();
						bd.Name = 'DB Test';
						bd.Structure__c = structure.id;
						bd.User_ID__c = 'uk07717417554@gmail.com';
						insert bd;
					// create New Grade 10 Girls
						New_Grade_10_Girls__c ng10g = New New_Grade_10_Girls__c();
						ng10g.school__c = school.id;
						ng10g.User_ID__c = 'uk07717417554@gmail.com';
						insert ng10g;
					// create Monitoring Visit
						Monitoring_Visit__c mv = New Monitoring_Visit__c();
						mv.MSG_Visiting__c = structure.id;
						mv.Primary_Monitor__c = contact.id;
						mv.school__c = school.id;
						mv.tm__c = contact.id;
						mv.User_ID__c = 'uk07717417554@gmail.com';
						insert mv;
					// create Camfed Annual Data
						Camfed_Annual_data__c cad = New Camfed_Annual_data__c();
						cad.school__c = school.id;
						cad.User_ID__c = 'uk07717417554@gmail.com';
						insert cad;
					for (Monitoring_Visit_Bursary__c mvpsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit_Bursary__c WHERE id = :mv.id]) {
            			if (mvpsms.User_ID_Phone__c != null) {
            			
            				toNumber = mvpsms.User_ID_Phone__c;
            				messageBody = mvpsms.SMSBody__c;
            					Map<String,String> params = new Map<String,String> {
				                	'From' => '+14156830489',
                					'To'   => toNumber,
                					'Body' => messageBody
             					};
         				
						}
    				}
					for (School_Termly_Update__c stusms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM School_Termly_Update__c WHERE id = :stu.id]) {
            			if (stusms.User_ID_Phone__c != null) {
            				toNumber = stusms.User_ID_Phone__c;
            				messageBody = stusms.SMSBody__c;
             					Map<String,String> params = new Map<String,String> {
                					'To'   => toNumber,
				               		'From' => '+14156830489',
                				 	'Body' => messageBody
				              	};                 
            			}
     				}
     				for (Structure_Status_Report__c ssrsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Structure_Status_Report__c WHERE id = :ssr.id]) {
            			if (ssrsms.User_ID_Phone__c != null) {
         				
             				toNumber = ssrsms.User_ID_Phone__c;
             				messageBody = ssrsms.SMSBody__c;
              					Map<String,String> params = new Map<String,String> {
                 					'To'   => toNumber,
                 					'From' => '+14156830489',
                  					'Body' => messageBody
              					};
        				
            			}
     				}
         			for (New_Cama_Review__c ncrsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM New_Cama_Review__c WHERE id = :ncr.id]) {
            			if (ncrsms.User_ID_Phone__c != null) {
            			
              				toNumber = ncrsms.User_ID_Phone__c;
              				messageBody = ncrsms.SMSBody__c;
              					Map<String,String> params = new Map<String,String> {
                  					'To'   => toNumber,
                  					'From' => '+14156830489',
                  					'Body' => messageBody
				               	};
        				
			           	}
			         }   		
         for (Background_data__c bdsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Background_data__c WHERE id = :bd.id]) {
            if (bdsms.User_ID_Phone__c != null) {
            
             	toNumber = bdsms.User_ID_Phone__c;
              	messageBody = bdsms.SMSBody__c;
               	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
               	};
        	
            }
         } 
         for (New_Grade_10_Girls__c ng10gsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM New_Grade_10_Girls__c WHERE id = :ng10g.id]) {
            if (ng10gsms.User_ID_Phone__c != null) {
           
             	toNumber = ng10gsms.User_ID_Phone__c;
             	messageBody = ng10gsms.SMSBody__c;
             	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
              	};           
        	
            }
         }
         for (Monitoring_Visit__c mvsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit__c WHERE id = :mv.id]) {
            if (mvsms.User_ID_Phone__c != null) {
           
              	toNumber = mvsms.User_ID_Phone__c;
             	messageBody = mvsms.SMSBody__c;
              	Map<String,String> params = new Map<String,String> {
                 	'To'   => toNumber,
                  	'From' => '+14156830489',
                  	'Body' => messageBody
              	};
       	 
           	}
         }  
         for (Camfed_Annual_data__c cadsms: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Camfed_Annual_data__c WHERE id = :cad.id]) {
            if (cadsms.User_ID_Phone__c != null) {
            
             	toNumber = cadsms.User_ID_Phone__c;
             	messageBody = cadsms.SMSBody__c;
            	Map<String,String> params = new Map<String,String> {
                   	'To'   => toNumber,
                   	'From' => '+14156830489',
                   	'Body' => messageBody
              	};
        	
            }  	  			  		
     	}		
	}			
}

 It deploys fine and validates fine just doesn't aid my code coverage for my main class.

 

What am I missing here?

 

Thanks

Dan

Best Answer chosen by Admin (Salesforce Developers) 
crop1645crop1645

Yes, the placement is correct

 

I'm kind of surprised the callout is being done in a testmethod as callouts can't be rolled back like SFDC DML.  I suspect the problem is in the setup to the callout

 

But, if you don't want to call Twilio at all in the testmethod, then you can do something easy like this:

 

public class SMSMVP {
	          @future (callout=true)
	          public static void sendSMS(Set<Id> Ids) {
	              String account = 'myaccountcode';
	              String token = 'myaccounttoken';
	              String toNumber = '';
	              String messsageBody = '';
	                  TwilioRestClient client = new TwilioRestClient(account, token);
	                      for (Monitoring_Visit_Bursary__c mvp: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit_Bursary__c WHERE id IN :Ids]) {
	                          if (mvp.User_ID_Phone__c != null) {
	                              toNumber = mvp.User_ID_Phone__c;
	                              messsageBody = mvp.SMSBody__c;
	                          Map<String,String> params = new Map<String,String> {
                              'To'   => toNumber,
	                              'From' => '+14156830489',
	                              'Body' => messsageBody
	                          };
	                  
if (!Test.isRunningTest()) 
   TwilioSMS sms = client.getAccount().getSMSMessages().create(params);
	                  }
	              }
	          }
 	  }

 If you need to actually inspect the results of the Twilio service, then you can build an interface that is by default the production interface and is switched in to be different by the testmethod. I've done this when testing inbound email handlers. The approach below was supplanted by the test.setMock solution I offered earlier that came out in a later SFDC version; below is the SFDC solution prior to Test.setMock

 

// Example using interfaces -- how to test HTTP callouts that otherwise won't run in a testMethod -- from Test Coverage and Best Practices webinar


public class AccountMashupController {
    private static String ACCOUNT_SERVICE_BASE_URL='some url';
    public Account account {get; set;}
    public List<String> debugMsgs {get; private set;}
    public Double totalTransactionAmt {get; set;}
    List<AccountTransaction> transactionList {get; set;}
    
	// !!!  Here is the Interface
    public IAccountTransactionService service {get; set;}

    //	Constructor
    public AccountMashupController(ApexPages.StandardController ctlr) {
       this.account =[select Name from Account where id=:ctlr.getRecord().id];
       service = new RESTAccountTransactionService();
    }


    public PageReference getTransactionDatav2() {
      debugMsgs = new List<String>();
      transactionList = new List<AccountTransaction>();
      Dom.Document doc = new Dom.Document();

      doc.load(service.execute(this.account.name));	// execute the service defined in the constructor

      Dom.XMLNode all Transactions = doc.getRootElement();
      totalTransactionAmt = 0.0;
      AccountTransaction acctTxn;
      for (Dom.XmlNode txn: allTransactions.getChildElements()) {
        acctTxn = new AccountTransaction(txn);
        totalTransactionAmt += acctTxn.amount;
        transactionList.add(acctTxn);
      }
     
      return null;
    }




// -----------Separate file: IAccountTransactionService ---------------
public interface IAccountTransactionService {
    String execute(String accountName); 					// methods that must be implemented
}

// -----------Interface impl #1 ---------------------------------------
public class MockAccountTransactionService implements IAccountTransactionService{
    public String execute (String accountName) {
    //  return mock data
    return '<?xml version="1.0" encoding=.....><transactions><id>AT-00000J</id><createddate>2011-03-24</createddate><amount>50.00</amount>....';
}


// ----------INterface impl #2 ------------------------
public class RESTAccountTransactionService implements IAccountTransactionService {
    public String execute(String accountname) {
    // .....make http callout here...return the result as a String //
}

//	-------------testMethod -------------
static testmethod void testTransactions() {
   Account acct = new Account(Name='test account');
   insert acct;
   
   ApexPages.StandardController stdCtlr = new ApexPages.StandardController(acct);
   AccountmashupController ctlr = new AccountMashupController(stdCtlr);

   //  Create the mock transaction instance that plays the role of the actual Http Rest Service
   MockAccountTransactionService mockService = new MockAccountTransactionService();
   cltr.service = mockService;   /// Key assignment here; when controller executes in this testmethod, it will use our overridden service

   Test.startTest();
   ctlr.getTransactionDatav2();
   Test.stopTest();
   System.assertEquals(...);

 

 

 

 

 

All Answers

crop1645crop1645

Dan --

 

your main class seems to use an @future annottaion. Per the SFDC doc, you need to wrap your testmethod in Test.startTest() - Test.stopTest()

 

To test methods defined with the future annotation, call the class containing the method in a startTest, stopTest code block. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously.

 So, no stopTest() means the test context never invokes the @future method.  Furthermore, your @future method is doing callouts, you may want to mock out the response as SFDC won't actually do the callout. See 

http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_classes_restful_http_testing_static.htm?SearchType=Stem

Daniel B ProbertDaniel B Probert

thanks eric thats good to know although I'm still having a major headache deploying my test apex and so now not even sure if i'm approaching it correctly.

 

this is a simplified apex class

public class SMSMVP {
	          @future (callout=true)
	          public static void sendSMS(Set<Id> Ids) {
	              String account = 'myaccountcode';
	              String token = 'myaccounttoken';
	              String toNumber = '';
	              String messsageBody = '';
	                  TwilioRestClient client = new TwilioRestClient(account, token);
	                      for (Monitoring_Visit_Bursary__c mvp: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit_Bursary__c WHERE id IN :Ids]) {
	                          if (mvp.User_ID_Phone__c != null) {
	                              toNumber = mvp.User_ID_Phone__c;
	                              messsageBody = mvp.SMSBody__c;
	                          Map<String,String> params = new Map<String,String> {
                              'To'   => toNumber,
	                              'From' => '+14156830489',
	                              'Body' => messsageBody
	                          };
	                  TwilioSMS sms = client.getAccount().getSMSMessages().create(params);
	                  }
	              }
	          }
 	  }

 i then call this from my object Monitoring_Visit_Bursary__c via this trigger:

trigger SMS_trg_Monitoring_Visit_Bursary on Monitoring_Visit_Bursary__c (after insert) {
            //Call the Apex class to Send out SMS messages
            SMSMVP.sendSMS(Trigger.newMap.keySet());
}

 and this works perfectly and I can deploy to my production - however it shows 0% coverage which obviously I want to resolve.

 

In order for me to create a record within my test class to invoke the trigger I need to create a record on Monitoring_Visit_Bursary object, in order for me to do this I need to create a test country/district/school & contact as they are all required fields on the MVB object.

 

Including what you have pointed me toward this is the test class for the above:

 

@isTest
public class SMSMVPTest {         
            static testMethod void sendSMS() {
                    // create a country
                        Country__c country = new Country__c();
                        country.Name = 'test country';
                        insert country;
                    // create a district
                        District__c district = new District__c(); 
                        district.Name = 'test';
                        district.Country__c = country.id;
                        insert district;
                    // create a school
                        School__c school = new School__c();
                        school.Name = 'test';
                        school.District__c = district.id;
                        insert school;
                    // create a contact
                        Contact contact = new Contact();
                        contact.LastName = 'test';
                        contact.District__c = district.id;
                        contact.School__c = school.id;
                        contact.Country__c = country.id;                       
                        insert contact;
                        test.startTest();
                    // create a Monitoring Visit to Bursary
                        Monitoring_Visit_Bursary__c mvp = new Monitoring_Visit_Bursary__c();
                        mvp.Person__c = contact.id;
                        mvp.User_ID__c = 'uk07717417554@gmail.com';
                        insert mvp;
                        test.stopTest();
    }           
}

 when i go to deploy my testclass I receive an error:

 

SMSMVPTest.sendSMS() Class 425  

 

Failure Message: "TwilioRestException: Script-thrown exception", Failure Stack Trace: "Class.TwilioRestClient: line 425, column 1 Class.TwilioResource.ListResource: line 440, column 1 Class.TwilioSmsList: line 74, column 1 Class.SMSMVP: line 18, column 1"

 

i'm totally stumped as to how to go about the mock callout? as i'm guessing that the error i'm getting there is related to not actually doing the call out?

 

cheers for your response.

 

dan

 

crop1645crop1645

Dan

 

First of all, your testmethod can be more compact as shown here:

 

@isTest
public class SMSMVPTest {         
            static testMethod void sendSMS() {
                    // create a country
                        Country__c country = new Country__c(name='test country');
                        insert country;
                    // create a district
                        District__c district = new District__c(name='test',country__c = country.id);
                        insert district;
                    // create a school
                        School__c school = new School__c(name='test',district__c = district.id);
                        insert school;
                    // create a contact
                        Contact contact = new Contact(lastname='test',district__c = district.id, school__c = school.id, country__c = country.id);
                        insert contact;
                        test.startTest();
                    // create a Monitoring Visit to Bursary
                        Monitoring_Visit_Bursary__c mvp = new Monitoring_Visit_Bursary__c(person__c = contact.id, user_id__c = 'uk07717417554@gmail.com');
                        insert mvp;
                        test.stopTest();
    }           
}

 As for your specific error, it looks more like a setup issue with Twilio (and I'm not familiar with this package). Your testmethod doesn't instantiate smsBody__c in the MVP object. Could that be it?

Daniel B ProbertDaniel B Probert

great thanks, nice to learn how to simplify my test code.

 

the reason SMSBody and User_ID_Phone aren't in the test is because there are a formula that feed off the User_ID field.

 

Basically they check for the first 2 charicters and fill in the test based on that information - so my thought was that just so long as I ensure User_ID is loaded then my trigger will fire which I believe it is.

 

Hence me being stumped by the problem it must be a twilio issue i'll see if I can find anything in their community that may point me in the right direction - it's annoying because the code itself works perfectly :(

 

cheers for your help.

 

dan

crop1645crop1645

Note that your testmethod is @isTest annotated which means no org data is used for the execution. It is possible that the Twilio code requires org data so as a lark, try @isTest(seeAlldata=true)

Daniel B ProbertDaniel B Probert
sadly no but worth a try i'm trying the Twilio IRC channel
crop1645crop1645

Dan

 

another idea - this is what my code does with callouts and testmethods

 

1. Add this class:

@isTest
public with sharing class MockHttpResponseGenerator implements HttpCalloutMock{
	//	Class used in testmethods to simulate HttpCallouts that are otherwise prohibited in test contexts	
	// 	Reference: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_restful_http_testing_httpcalloutmock.htm
	
	private	String	headerKey;
	private String	headerVal;
	private Integer	statusCode			= 200;
	private String	body;
	private String	status				= 'OK';
	
	public HTTPResponse respond(HTTPRequest req) {
		HttpResponse	res				= new HttpResponse();
		if (this.headerKey != null)
			res.setHeader(this.headerKey,this.headerVal);
		if (this.body != null)
			res.setBody(this.body);
		res.setStatus(this.status);
		res.setStatusCode(this.statusCode);
		return res;
		
		
	}
	//	-------------------------------------------
	//	Constructor -- allows testmethod caller to specify the mock results; optional
	//	------------------------------------------
	public MockHttpResponseGenerator() {}
	public MockHttpResponseGenerator(String body, String headerKey, String headerVal, Integer statusCode, String status) {
		this.body				= body;
		this.headerKey			= headerKey;
		this.headerVal			= headerVal;
		this.statusCode			= statusCode	!= null ? statusCode : this.statusCode;
		this.status				= status 		!= null ? status	 : this.status;
	}
}

 2. And your testmethod does the following:

Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());

 which must be after the Test.startTest() and before the Test.stoptest()

 

This gives the apex test context something to invoke when it does a callout; everything is dummy  here as it assumes you don;t care about testing a mock response from Twilio

 

Daniel B ProbertDaniel B Probert

i've just tried this but getting the same error when deploying the testclass. i was able to deploy the calloutmock without an issue.

just to confirm this is what you mean by where to put the line:

 

Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());

 this what my test class now looks like:

 

@isTest
    private class SMSMVPTest {
        @isTest Static void sendSMStest() { 
                // create a country 
                Country__c country = new Country__c(name='test country'); 
                insert country; 
                // create a district 
                District__c district = new District__c(name='test',country__c = country.id); 
                insert district; 
                // create a school 
                School__c school = new School__c(name='test',district__c = district.id); 
                insert school; 
                // create a contact 
                Contact contact = new Contact(lastname='test',district__c = district.id, school__c = school.id, country__c = country.id); 
                insert contact; 
                test.startTest(); 
                Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());
                // create a Monitoring Visit to Bursary 
                Monitoring_Visit_Bursary__c mvp = new Monitoring_Visit_Bursary__c(person__c = contact.id, user_id__c = 'uk07717417554@gmail.com'); 
                insert mvp; 
                test.stopTest(); 
                } 
        }

 someone on the twilio IRC channel said it's twilio giving an error response so all i've got to figure out if how to stop it from doing the twilio callout and it should be fine :)

crop1645crop1645

Yes, the placement is correct

 

I'm kind of surprised the callout is being done in a testmethod as callouts can't be rolled back like SFDC DML.  I suspect the problem is in the setup to the callout

 

But, if you don't want to call Twilio at all in the testmethod, then you can do something easy like this:

 

public class SMSMVP {
	          @future (callout=true)
	          public static void sendSMS(Set<Id> Ids) {
	              String account = 'myaccountcode';
	              String token = 'myaccounttoken';
	              String toNumber = '';
	              String messsageBody = '';
	                  TwilioRestClient client = new TwilioRestClient(account, token);
	                      for (Monitoring_Visit_Bursary__c mvp: [SELECT Id, SMSBody__c, User_ID_Phone__c FROM Monitoring_Visit_Bursary__c WHERE id IN :Ids]) {
	                          if (mvp.User_ID_Phone__c != null) {
	                              toNumber = mvp.User_ID_Phone__c;
	                              messsageBody = mvp.SMSBody__c;
	                          Map<String,String> params = new Map<String,String> {
                              'To'   => toNumber,
	                              'From' => '+14156830489',
	                              'Body' => messsageBody
	                          };
	                  
if (!Test.isRunningTest()) 
   TwilioSMS sms = client.getAccount().getSMSMessages().create(params);
	                  }
	              }
	          }
 	  }

 If you need to actually inspect the results of the Twilio service, then you can build an interface that is by default the production interface and is switched in to be different by the testmethod. I've done this when testing inbound email handlers. The approach below was supplanted by the test.setMock solution I offered earlier that came out in a later SFDC version; below is the SFDC solution prior to Test.setMock

 

// Example using interfaces -- how to test HTTP callouts that otherwise won't run in a testMethod -- from Test Coverage and Best Practices webinar


public class AccountMashupController {
    private static String ACCOUNT_SERVICE_BASE_URL='some url';
    public Account account {get; set;}
    public List<String> debugMsgs {get; private set;}
    public Double totalTransactionAmt {get; set;}
    List<AccountTransaction> transactionList {get; set;}
    
	// !!!  Here is the Interface
    public IAccountTransactionService service {get; set;}

    //	Constructor
    public AccountMashupController(ApexPages.StandardController ctlr) {
       this.account =[select Name from Account where id=:ctlr.getRecord().id];
       service = new RESTAccountTransactionService();
    }


    public PageReference getTransactionDatav2() {
      debugMsgs = new List<String>();
      transactionList = new List<AccountTransaction>();
      Dom.Document doc = new Dom.Document();

      doc.load(service.execute(this.account.name));	// execute the service defined in the constructor

      Dom.XMLNode all Transactions = doc.getRootElement();
      totalTransactionAmt = 0.0;
      AccountTransaction acctTxn;
      for (Dom.XmlNode txn: allTransactions.getChildElements()) {
        acctTxn = new AccountTransaction(txn);
        totalTransactionAmt += acctTxn.amount;
        transactionList.add(acctTxn);
      }
     
      return null;
    }




// -----------Separate file: IAccountTransactionService ---------------
public interface IAccountTransactionService {
    String execute(String accountName); 					// methods that must be implemented
}

// -----------Interface impl #1 ---------------------------------------
public class MockAccountTransactionService implements IAccountTransactionService{
    public String execute (String accountName) {
    //  return mock data
    return '<?xml version="1.0" encoding=.....><transactions><id>AT-00000J</id><createddate>2011-03-24</createddate><amount>50.00</amount>....';
}


// ----------INterface impl #2 ------------------------
public class RESTAccountTransactionService implements IAccountTransactionService {
    public String execute(String accountname) {
    // .....make http callout here...return the result as a String //
}

//	-------------testMethod -------------
static testmethod void testTransactions() {
   Account acct = new Account(Name='test account');
   insert acct;
   
   ApexPages.StandardController stdCtlr = new ApexPages.StandardController(acct);
   AccountmashupController ctlr = new AccountMashupController(stdCtlr);

   //  Create the mock transaction instance that plays the role of the actual Http Rest Service
   MockAccountTransactionService mockService = new MockAccountTransactionService();
   cltr.service = mockService;   /// Key assignment here; when controller executes in this testmethod, it will use our overridden service

   Test.startTest();
   ctlr.getTransactionDatav2();
   Test.stopTest();
   System.assertEquals(...);

 

 

 

 

 

This was selected as the best answer
Daniel B ProbertDaniel B Probert

eric i can't thank you enough i 100% owe you a beer for this one.

 

the code coverage is 88% as well which is good enough for me :)

 

thank you so much for keep responding and helping me get through this one learnt alot.

 

cheers

dan