How do I test a trigger with an approval process? - triggers

I have a trigger which initiates an approval process when certain criteria are met:
trigger AddendumAfterIHMS on Addendum__c (after update) {
for (integer i = 0; i<Trigger.new.size(); i++){
if(Trigger.new[i].RecordTypeId != '012V0000000CkQA'){
if(Trigger.new[i].From_IHMS__c != null && Trigger.old[i].From_IHMS__c == null){
ID addendumId = Trigger.new[i].Id;
// Start next approval process
Approval.ProcessSubmitRequest request = new Approval.ProcessSubmitRequest();
request.setObjectId(addendumId);
Approval.ProcessResult requestResult = Approval.process(request);
}
}
}
}
It works perfectly, but now i need to create a test class for it. I have created a class which brings the code up to 75% coverage, which is the minimum, but I'm picky and like to have 100% coverage on my code. The test class I have now gets stuck on the line request.setObjectId(addendumId); and doesn't move past it. The error I receive is:
System.DmlException: Update failed. First exception on row 0 with id a0CV0000000B8cgMAC; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, AddendumAfterIHMS: execution of AfterUpdate
Here is the test class that I have written so far, most of the class actually tests some other triggers, but the important line which is throwing the error is the very last line update addendumTierFeature;
#isTest
private class AddendumTest {
static testMethod void myUnitTest() {
// Query Testing Account, will need ID changed before testing to place into production
Account existingAccount = [SELECT Id FROM Account LIMIT 1];
Model__c existingModel = [SELECT Id FROM Model__c WHERE Active__c = TRUE LIMIT 1];
Pricebook2 existingPricebook = [SELECT Id,Name FROM Pricebook2 WHERE IsActive = TRUE LIMIT 1];
List<Contact> existingContacts = [SELECT Id,Name FROM Contact LIMIT 2];
Contact existingContactPrimary = existingContacts[0];
Contact existingContactSecondary = existingContacts[1];
Opportunity newOpportunity = new Opportunity(
Name = 'New Opportunity',
Account = existingAccount,
CloseDate = Date.today(),
Order_Proposed__c = Date.today(),
StageName = 'Branch Visit - Not Responding',
Opportunity_Follow_Up__c = 'Every 120 Days',
LeadSource = 'Farm Lists',
Source_Detail__c = 'FSBO',
Model_Name__c = existingModel.Id,
Processing_Fee__c = 100.50,
Site_State__c = 'OR',
base_Build_Zone__c = 'OR',
Pricebook_from_Lead__c = existingPricebook.Name
);
insert newOpportunity;
//system.assert(newOpportunity.Id != null);
ID newOppId = newOpportunity.Id;
OpportunityContactRole contactPrimary = new OpportunityContactRole(
Role = 'Primary',
IsPrimary = true,
OpportunityId = newOppId,
ContactId = existingContactPrimary.Id
);
OpportunityContactRole contactSecondary = new OpportunityContactRole(
Role = 'Primary',
IsPrimary = false,
OpportunityId = newOppId,
ContactId = existingContactPrimary.Id
);
insert contactPrimary;
insert contactSecondary;
newOpportunity.Name = 'Different - Updating';
newOpportunity.Order_Accepted__c = Datetime.now();
update newOpportunity;
Addendum__c addendumCustomOption = new Addendum__c(
RecordTypeId = '012V0000000CkQA', //Pre Priced Custom Option
Opportunity__c = newOppId,
Item_Pre_Priced_Description__c = 'a1eV00000004DNu',
Reason__c = 'This is a reason',
Item__c = 'This is an Item',
Quantity__c = 1
);
Addendum__c addendumTierFeature = new Addendum__c(
RecordTypeId = '012V0000000Cjks', //Tier Feature
Opportunity__c = newOppId,
Category__c = 'Countertops',
Reason__c = 'This is a reason',
Item__c = 'This is an Item',
Quantity__c = 1
);
insert addendumCustomOption;
insert addendumTierFeature;
addendumCustomOption.Quantity__c = 2;
addendumTierFeature.Quantity__c = 2;
update addendumCustomOption;
update addendumTierFeature;
update newOpportunity;
addendumTierFeature.To_IHMS__c = system.now();
update addendumTierFeature;
addendumTierFeature.From_IHMS__c = system.now();
update addendumTierFeature;
}
}
Any help on this matter would be greatly appreciated. I believe the problem is in the way I am testing the approval process start. Is there by chance a special testing function for this?

After fiddling around for a little while I discovered that the error was actually tied into my approval process. I kept digging into the error logs until I got to the error: caused by: System.DmlException: Process failed. First exception on row 0; first error: MANAGER_NOT_DEFINED, Manager undefined.: []. This phrase indicates that there is no one defined for the next step in my approval process.
When I created the opportunity, I did not set the owner and somehow this created an opportunity which had an owner without a manager. The addendum was also created without an owner/manager. So when I tried to launch the next approval process, there was no manager to send the approval to and an error was thrown.

Related

Apex class is called in a trigger based on some conditions.Test class passes the test but code coverage is stil 0%

When work order updates to Completed then a new water meter reading record needs to created with two field values from work order and two from another object att. Both work order and water meter reading have a look up(workorder) and master detail relation(water meter reading) with att.Sorry for so much code,but I am really stuck and need help.
trigger CreateWaterMeterReading on sm1e__smWork_Order__c (after update)
{
if (Trigger.new.size() == 1)
{
sm1e__smWork_Order__c wo = Trigger.new[0];
if(wo.sm1e__WO_Type__c == 'Meter Read Move In/Out ' && wo.sm1e__Status__c == 'Completed')
reateNewWaterMeterRead.createWMRforMoveInOrOut(wo.Id);
}
}
--Apex class
public class CreateNewWaterMeterRead {
public static void createWMRforMoveInOrOut(string workorderId)
{
Work_Order__c wo = [Select Equipment__r.Name,Completion_Date__c,Meter_Reading__c from Work_Order__c where Id = : workorderId ];
Equipment__c att = [Select Id,Last_Water_Meter_Reading_Date__c,Last_Water_Meter_Reading__c from Equipment__c where Name = : wo.Equipment__r.Name ];
List<Water_Meter_Readings__c> newwmr = new List<Water_Meter_Readings__c>();
Water_Meter_Readings__c wmr = new Water_Meter_Readings__c();
wmr.Meter__c = att.Id;
wmr.Current_Meter_Reading__c = wo.Meter_Reading__c;
wmr.Current_Read_Date__c = wo.sm1e__Completion_Date__c;
wmr.Prior_Meter_Reading__c = att.Last_Water_Meter_Reading__c;
wmr.Prior_Read_Date__c = att.Last_Water_Meter_Reading_Date__c;
wmr.Source__c = 'Manual Read';
newwmr.add(wmr);
if(newwmr.size() >0)
insert newwmr;
}
--Test Class
isTest(SeeAllData = true)
public class CreateNewWaterMeterReadTest
{
static testmethod void createWMRforMoveInOrOut()
{
Work_Order__c wo = [Select Id,Equipment__r.Name,Completion_Date__c,Meter_Reading__c from Work_Order__c where sm1e__Status__c != 'Completed' AND sm1e__WO_Type__c = 'Meter Read Move In/Out' LIMIT 1];
Equipment__c att = [Select Id,Last_Water_Meter_Reading_Date__c,Last_Water_Meter_Reading__c from Equipment__c where Name = : wo.Equipment__r.Name ];
test.startTest();
wo.Meter_Reading__c = 1317;
wo.sm1e__Status__c = 'Completed';
update wo;
test.stopTest();
System.debug('updated wo');
Water_Meter_Readings__c wmr = new Water_Meter_Readings__c();
System.debug('wmr for test');
wmr.Meter__c= att.Id;
wmr.Current_Meter_Reading__c = wo.Meter_Reading__c;
wmr.Current_Read_Date__c = wo.Completion_Date__c;
System.debug('in between wmr');
wmr.Prior_Meter_Reading__c = att.Last_Water_Meter_Reading__c;
wmr.Prior_Read_Date__c = att.Last_Water_Meter_Reading_Date__c;
wmr.Source__c = 'Manual';
insert wmr;
You've written trigger on after update. But in your test class you're inserting a record and not updating it. That is why your code coverage is 0%. Refer to this link
For executing trigger you must do dml operation for which you've written a trigger. Also you can invoke your class method from test class by creating its instance. Refer above link for the same.

Trigger Help: Capture user from custom object (ticket) and assign task to user (task object)

I am very new to apex coding. Please see trigger below which creates a task when a ticket is raised with priority high. The trigger works except for this line : // t.Owner = Tickets__c.Assigned_To__c;
I am trying to pick up the the user to whom the ticket is assigned via field Assigned_To__c in the custom ticket object and then create a task where the owner of the task is the same user as the 'Assigned to' field captures in the ticket object.
What do I need to do to pick up the user captured in a field (Assigned to) in a custom object (ticket) and then assign the same user as owner of a task?
Trigger compiles except for the commented line:
trigger AssignTicket on Tickets__c (after insert,after update) {
for(Tickets__c tkt : trigger.new){
if(tkt.Priority__c == 'High'){
task t = new task();
t.Subject = 'Ticket has been assigned to you!';
t.Status = 'Not Started';
t.Priority = 'Normal';
//t.Owner = Tickets__c.Assigned_To__c.; **[Need help with this line]**
t.WhatId = tkt.id;
insert t;
}
}
Take the insert out of the loop and set OwnerID, not Owner.
List<Task> lInsert = new List<Task>();
for(Tickets__c tkt : trigger.new){
if(tkt.Priority__c == 'High'){
task t = new task();
t.Subject = 'Ticket has been assigned to you!';
t.Status = 'Not Started';
t.Priority = 'Normal';
t.OwnerID = Tickets__c.Assigned_To__c.; **[Need help with this line]**
t.WhatId = tkt.id;
lInsert.Add(t);
}
}
insert lInsert;

Test Coverage fails on before insert / before update Apex trigger

I have this very simple before insert / update trigger on Opportunity that auto-selects the Price Book based on a dropdown value containing Sales Office (State) location info.
Here's my Trigger:
trigger SelectPriceBook on Opportunity ( before insert, before update ) {
for( Opportunity opp : Trigger.new ) {
// Change Price Book
// New York
if( opp.Campus__c == 'NYC' )
opp.Pricebook2Id = PB_NYC; // contains a Pricebook's ID
// Atlanta
if( opp.Campus__c == 'ATL' )
opp.Pricebook2Id = PB_ATL; // contains another Pricebook's ID
}
}
Here's my Test Class:
#isTest (SeeAllData = true)
public class SelectPriceBookTestClass {
static testMethod void validateSelectPriceBook() {
// Pricebook IDs
ID PB_NYC = 'xxxx';
ID PB_ATL = 'xxxx';
// New Opp
Opportunity opp = new Opportunity();
opp.Name = 'Test Opp';
opp.Office__c = 'NYC';
opp.StageName = 'Quote';
// Insert
insert opp;
// Retrive inserted opportunity
opp = [SELECT Pricebook2id FROM Opportunity WHERE Id =:opp.Id];
System.debug( 'Retrieved Pricebook Id: ' + opp.Pricebook2Id );
// Change Campus
opp.Office__c = 'ATL';
// Update Opportunity
update opp;
// Retrive updated opportunity
opp = [SELECT Pricebook2id FROM Opportunity WHERE Id =:opp.Id];
System.debug( 'Retrieved Updated Pricebook Id: ' + opp.Pricebook2Id );
// Test
System.assertEquals( PB_ATL, opp.Pricebook2Id );
}
}
The test runs report 0% test coverage.
Also, on similar lines I have another before insert trigger that sets the Owner of an Event same as the Owner of the parent Lead. Here's the code:
trigger AutoCampusTourOwner on Event( before insert ) {
for( Event evt : Trigger.new ) {
// Abort if other kind of Event
if( evt.Subject != 'Visit' )
return;
// Set Owner Id
Lead parentLead = [SELECT OwnerId FROM Lead WHERE Id = :evt.WhoId];
evt.OwnerId = parentLead.OwnerId;
}
}
This, too, is causing 0% coverage - my guess is that it's got something to do with the for loops in both. I know I'm seriously flouting DML rules by invoking SOQL query inside a for loop, but for my purposes it should be fine as these Events are created manually and only one at a time - so there are no scopes of governor limits kicking in due to bulk inserts.
The code in both cases work 100%. Please suggest a fix for the test cases.
Have you tried trigger.old ?? My thinking is, when you update the office in your test class from NYC to ATL, the value 'NYC' will be in trigger.old, and that's what you want to check in your trigger.
I could be wrong since i'm new to apex too, but try it and let me know what happens.
For the first trigger don't do anything rather just create opportunity and execute like this.
Test class for SelectPriceBook
#isTest
private class TriggerTestClass {
static testmethod void selectPriceTest(){
Opportunity opps = new Opportunity(
Name= 'Test Opps',
CloseDate = System.today().addDays(30),
StageName = 'Prospecting',
ForecastCategoryName = 'Pipeline',
Office__c = 'NYC');
insert opps;
Opportunity opps2 = new Opportunity(
Name= 'Test Opps 2',
CloseDate = System.today().addDays(28),
StageName = 'Prospecting',
ForecastCategoryName = 'Pipeline',
Office__c = 'ATL');
insert opps2;
}
}
It will give you good test coverage and I don't know what are you trying to do in AutoCampusTourOwner!
i have test class for these
trigger ClientEmailTrigger on inflooens__Client_Email__c (after insert, after update, before insert, before update) {
ApexTriggerSettings__c setting = ApexTriggerSettings__c.getValues('Inflooens Trigger Settings');
if(setting != NULL && setting.ClientEmailTrigger__c == TRUE){
AuditTrailController objAdt = new AuditTrailController();
if(Trigger.isAfter){
if(Trigger.isUpdate){
System.debug('In Update Client Email Record');
objAdt.insertAuditRecord(Trigger.newMap, 'inflooens__Client_Email__c', Trigger.new.get(0).Id, Trigger.oldMap);
}
if(Trigger.isInsert){
objAdt.insertAuditRecord(Trigger.newMap, 'inflooens__Client_Email__c', null , null);
}
}
}
}

Apex - Salesforce.com - I need help writing an APEX class to test my working trigger - Chatter - Monitor specific keywords

Here is my working trigger
trigger CheckChatterPostsOnNSP on FeedItem (before insert) {
Set<Id> nspIds = new Set<Id>();
//Get the NSP that will be updated
List<Non_Standard_Pricing__c> nsp2Update = new List<Non_Standard_Pricing__c>();
//Get the key prefix for the NSP object via a describe call.
String nspKeyPrefix = Non_Standard_Pricing__c.sObjectType.getDescribe().getKeyPrefix();
//Get the Id of the user
Id profileId = UserInfo.getProfileId();
for (FeedItem f: trigger.new) {
String parentId = f.parentId;
if(profileId == '00e30000000eWXR') {// Users profile must be Sales and Service
//We compare the start of the 'parentID' field to the NSP key prefix to
//restrict the trigger to act on posts made to the NSP object.
if (
parentId.startsWith(nspKeyPrefix) &&
(
f.Body.contains('***APPROVED BY CHANNEL***') ||
f.Body.contains('***APPROVED BY CSM***') ||
f.Body.contains('[APPROVED BY CHANNEL]') ||
f.Body.contains('[APPROVED BY CSM]')
)
){
nspIds.add(f.parentId);
}
}
}
List < Non_Standard_Pricing__c > nsps = [select id, Pre_Approved_Service_Discount__c, ownerId
from Non_Standard_Pricing__c where id in :nspIds];
for (Non_Standard_Pricing__c n: nsps) {
//We compare the creator of the Chatter post to the NSP Owner to ensure
//that only authorized users can close the NSP using the special Chatter 'hot-key'
n.Pre_Approved_Service_Discount__c = true;
nsp2Update.add(n);
}
update nsp2Update;
}
Here is my attempt to write an APEX Test Class
#isTest
public class CheckChatterPostsOnNSPTEST {
static testMethod void CheckChatterPostsOnNSPTEST() {
//Create and insert opp
Opportunity opp = new Opportunity(Name='test opp', StageName='stage', Probability = 95, CloseDate=system.today());
insert opp;
//Create and insert NSP
Non_Standard_Pricing__c NSP = new Non_Standard_Pricing__c(Opportunity__c = opp.Id, Status__c = 'Open');
insert NSP;
//Find user with Profile = Sales and Service
Profile SalesNService = [Select id from Profile where Name = 'Sales and Service' limit 1];
User u = new User(
Alias = 'standt',
Email='standarduser#testorg.com',
EmailEncodingKey='UTF-8',
LastName='Testing',
LanguageLocaleKey='en_US',
LocaleSidKey='en_US',
ProfileId = SalesNService.Id,
TimeZoneSidKey='America/Los_Angeles',
UserName='standarduser#testorg.com'
);
System.runAs(u)
{
//Create FeedItem entry with text '[APPROVED BY CHANNEL]'
FeedItem post = new FeedItem();
post.body = '[APPROVED BY CHANNEL]';
//Now update the opportunites to invoke the trigger
Test.startTest();
insert post;
Test.stopTest();
}
//Assertion Testing
for(Opportunity o : [select Id, Name, Primary_NSP__r.Pre_Approved_Service_Discount__c from Opportunity where Id = :opp.Id]){
system.assert(o.Primary_NSP__r.Pre_Approved_Service_Discount__c = true);
}
}
}
I'm getting the following errors
Message: System.QueryException: List has no rows for assignment to SObject
Stack Trace: Class.CheckChatterPostsOnNSPTEST.CheckChatterPostsOnNSPTEST: line 14, column 1
Any help is greatly appreciated.
That'd be pointing to this line:
Profile SalesNService = [Select id from Profile where Name = 'Sales and Service' limit 1];
Simply check if this query returns something? Typo in the profile name (maybe you have them with underscores or "Sales & Service")? Maybe there's no such profile in org at all (for example if you've created such one on production but the sandbox you're in was not refreshed afterwards)?
I'm afraid we can't help you more than that ;) It can't be even related to API versions, "seeAllData" etc because docs say Profiles are still visible.

Apex Trigger Works, can not get test pass

I came back to this 3 hours later and with no changes to any code, test execution worked.
Disregard This Question
I have written a trigger that works great for setting the accountID on a contact record based on an external ID.
I had a test that worked great, but I added some logic to the trigger so it only updates contacts with a 'Default Account' as account. Now my test fails saying it did not update the account even though it still works as designed when saving records.
What am I missing?
I added line 4
ID DefaultAccID = [select ID from Account where Name = 'Default Account'].ID;
and the if around line 9
if (contactNew.AccountID == DefaultAccID) {
}
to Trigger:
trigger TRG_Contact_SetHouseholdID on Contact (before update, before insert) {
Set<String> AccIDlist = new Set<String>();
ID DefaultAccID = [select ID from Account where Name = 'Default Account'].ID;
// loop through records finding thoes that need to be linked
for(Contact contactNew:Trigger.New){
if (contactNew.AccountID == DefaultAccID) {
AccIDlist.add(contactNew.RPHouseholdID__c);
}
}
Map<String, ID> AccountMap = new Map<String, ID>();
//loop needing linked and get account ID if it exist
for(Account oneAccount:[SELECT RPHouseholdID__c, ID from Account where RPHouseholdID__c IN :AccIDlist]){
AccountMap.put(oneAccount.RPHouseholdID__c, oneAccount.ID);
}
// loop through records updating the ones that need link
for(Contact contactNew:Trigger.New){
if (AccountMap.get(contactNew.RPHouseholdID__c) <> null){
contactNew.AccountID = AccountMap.get(contactNew.RPHouseholdID__c);
}
}
}
Test Class:
#isTest
Private class TRG_Contact_SetHouseholdID_Test {
static TestMethod void Test0_TestInsertWithValue(){
insertTestAccounts();
Account defaultAccount = [select ID from Account where name = 'Default Account' limit 1];
Account newAccount = [select ID from Account where name = 'Camper Family' limit 1];
test.startTest();
insertTestContact(defaultAccount.ID);
test.stopTest();
Contact newContact = [select ID,AccountID from Contact where firstname = 'Happy' and lastname = 'Camper' limit 1];
System.assertEquals(newContact.AccountID, newAccount.ID, 'accountID did not get changed from default (' + defaultAccount.ID + ')');
}
private static void insertTestAccounts()
{
Account obj = new Account();
obj.Name = 'Default Account';
insert obj;
obj = new Account() ;
obj.Name = 'Camper Family';
obj.RPHouseholdID__c = '111';
insert obj;
}
private static void insertTestContact(ID defaultID)
{
Contact obj = new Contact();
obj.AccountID = defaultID;
obj.firstName = 'Happy';
obj.lastname = 'Camper';
obj.RPHouseholdID__c = '111';
insert obj;
}
}
Expanding a bit on #zachelrath 's answer, beginning with API version 24.0, test methods are isolated from the data in the organization (with a few exceptions User, RecordType, etc. see here for the full list). Rather than changing the API version of the file, the easiest thing to do is to add the see all data directive to the isTest annotation, like so:
#isTest(SeeAllData=true)
Private class TRG_Contact_SetHouseholdID_Test {
// etc
Set your test class' API version to 24.0 --- I'd bet your initial query for the Default Account is pulling in a preexisting record whose Name is 'Default Account', instead of the Default Account you're creating in your test data. There are 2 ways to get around this:
(Easiest) Change your test class to API version 24.0
If you would rather stay in API 23.0 or lower, then, at the start of your test method, delete all accounts named 'Default Account', then insert your test accounts.