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.
Related
There is two object: Fund and opportunity. and opportunity object has a lookup of Fund. I've developed one trigger that sums up the total amount of all the relative opportunity and set that amount in a custom field of fund object. now a problem in my code is that whenever I try to create bulk opportunity using CSV file that contains data for multiple funds at that time it sums up the total of all fund and set that only in first record fund ID. I need a solution using Map.
Thank you.
Trigger:
trigger newLeadTrigger on Opportunity (after insert , after update, after delete , after undelete) {
if(trigger.isAfter && (trigger.isInsert || trigger.isUpdate || trigger.isUndelete)){
OpportunityCustomRollup.CountRollup(Trigger.new);
}
if(Trigger.isDelete)
{
OpportunityCustomRollup.CountRollup(Trigger.old);
}
}
Controller class:
public class OpportunityCustomRollup {
public static void CountRollup(List<Opportunity> lstOpportunity){
set<id> oppIds = new set<id>();
map<string, integer> classroomIDToDeskCountMap = new map<string, integer>();
id objrecordtypeid = [SELECT Id FROM RecordType WHERE DeveloperName ='Fund_Raising'].Id;
double amount = 0;
try {
for (Opportunity objOpportunity : lstOpportunity){
oppIds.add(objOpportunity.Fund__c);
}
Fund__c objfund = [SELECT Id, Total_opportunity_amount__c FROM Fund__c WHERE Id = :oppIds];
List<Opportunity> list_Opportunity = [SELECT Id, Amount FROM Opportunity WHERE Fund__c = :objfund.Id and StageName = 'Closed Won' and RecordTypeId =: objrecordtypeid];
for(Opportunity AmountOpportunity : list_Opportunity) {
amount += AmountOpportunity.amount;
}
objfund.Total_opportunity_amount__c = amount;
update objfund;
}
catch (Exception e) {
System.debug(e);
}
}
}
This should give you some ideas but you'll have to experiment a bit yourself. Edit your question with updated code and drop me a comment if you're still stuck.
// This is just for testing because when executing standalone apex code
// you don't have trigger.new. Modify the query as you want.
List<Opportunity> lstOpportunity = [SELECT Id
FROM Opportunity
WHERE StageName = 'Closed Won' and RecordType.DeveloperName = 'Fund_Raising'
LIMIT 10];
List<Fund__c> funds = new List<Fund__c>();
for(AggreateResult ar : [SELECT SUM(Amount) amt, Fund__c fund
FROM Opportunity
WHERE Id IN :lstOpportunity
AND StageName = 'Closed Won' and RecordType.DeveloperName = 'Fund_Raising'
AND Fund__c != null
GROUP BY Fund__c]){
Fund__c f = new Fund__c(
Id = (Id) ar.get('fund'),
Total_opportunity_amount__c = (Decimal) ar.get('amt')
);
funds.add(fund);
}
update funds;
P.S. Check this app on appexchange: https://appexchange.salesforce.com/appxListingDetail?listingId=a0N30000009i3UpEAI
I'm not affiliated with them but if you're an admin it might be easier to configure it than to write code, unit tests..
I just wrote this trigger and it seems to be working fine in dev, I need to move it into production, however, the test class I wrote passes the test but does not cover the trigger. Any help would be greatly appreciated. I am a bit green here. I know I should be inserting a contact (the account is a req field) then updating the contact field I just have no earthly clue how to do taht. Thank you
trigger PropOwned on Contact (after update) {
for (Contact c : Trigger.new) {
McLabs2__Ownership__c ownNew = new McLabs2__Ownership__c();
Contact oldContact = Trigger.oldMap.get(c.id);
if (c.One_Prop_Owned__c != oldContact.One_Prop_Owned__c && c.One_Prop_Owned__c != null) {
ownNew.McLabs2__Contact__c = c.id;
ownNew.McLabs2__Property__c = c.One_Prop_Owned__c;
insert ownNew;
}
}
}
This is the test class I wrote.
#isTest
public class TestOwnership {
static testMethod void createOwnership() {
McLabs2__Ownership__c ownNew = new McLabs2__Ownership__c();
ownNew.McLabs2__Contact__c = 'Michael Webb';
ownNew.McLabs2__Property__c = '131 West 33rd Street';
insert ownNew;
}
}
Your test class just creates a McLabs2__Ownership__c object and inserts this object in database. As a result of this trigger on McLabs2__Ownership__c(if exist) will be invoked, but you have to test a trigger on Contact object. Thus you need to insert an account and after that update it because your contact trigger works in after update mode.
So, you need something like that
#isTest
private class TestOwnership {
static testMethod void whenContactUpdatedNewOwnershipIsInserted() {
// create contact, you have to replace 'value1' with appropriate data type
Contact contact = new Contact(name = 'Test Contact', One_Prop_Owned__c = 'value1');
insert contact;
contact.One_Prop_Owned__c = 'value2'; // you have to replace value2 with appropriate data type
update contact;
// in this place you should has only one record of McLabs2__Ownership__c in database, because in test context real data isn't visible
List<McLabs2__Ownership__c> ownerships = [SELECT Id, McLabs2__Contact__c, McLabs2__Property__c FROM McLabs2__Ownership__c];
System.assertEquals(1, ownerships.size());
System.assertEquals(contact.Id, ownerships[0].McLabs2__Contact__c);
System.assertEquals(contact.One_Prop_Owned__c, ownerships[0].McLabs2__Property__c);
}
}
Read the following articles which might be pretty useful for you:
Apex Trigger best practice
SOQL in loop
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);
}
}
}
}
I want to set a Lookup Field using a salesforce apex trigger, but I keep getting an error:
System.StringException: Invalid id:
I have a custom object called Job__c. It has a custom pick list for accounts: Acct__c.
A user will populate Acct__c as John Deer, the trigger should add John Deer to the Account__c lookup field.
Here is the trigger:
trigger UpdateAccounts on Job__c (before insert) {
for (Job__c obj: trigger.new){
obj.Account__c = obj.Acct__c; //Exception is thrown here
}
Exception thrown:
System.StringException: Invalid id:
I tried something different:
List <Job__c> opListInsert = new List<Job__c>();
List <Job__c> opListUpdate = new List<Job__c>();
if(trigger.isInsert){
for(Job__c op:trigger.New){
if(op.Acct__c != Null){
op.Account__c = op.Acct__c;
opListInsert.add(op);
}
}
}
else if(trigger.isUpdate){
for(Job__c op:trigger.New){
if(op.Acct__c != Null && op.Acct__c !=trigger.oldMap.get(op.id).Acct__c){
op.Account__c = op.Acct__c;
opListUpdate.add(op);
}
}
}
That code throws:
Error:Apex trigger UpdateAccounts caused an unexpected exception, contact your
administrator: UpdateAccounts: execution of BeforeUpdate caused by:
System.StringException: Invalid id:
What am I doing wrong that it tells me it's an invalid ID?
Account__c is a look-up field, you cannot assign a string (the picklist field) to it. Instead you have to assign it an account id.
I am not sure why you're using the account names in the picklist field, but if you want to continue doing that then there's a simple solution that the account names are unique.
Get the id for the account selected in the query for the id, something like this:
list<account> acclist = [select id from account where name In yourNameList];
And then in the trigger reference the id
obj.account__c = accList[0].id;
Make use of maps , do not add the soql's inside for loop
You have to set your field with the ID of the object you want it to link to.
The salesforce browser page has a handy dandy tool that lets you put in incomplete information into the lookup field, and it fills it in for you automatically. This does not happen when values are assigned manually with an apex statement. You can only set the value to a valid and existing ID of the object the lookup was set to link to.
This code will grab the ID given name and do what you wanted:
trigger UpdateAcct on Job__c (before insert, before update) {
List<String> Accounts = new List<String>();
for (Job__c obj: trigger.new){
Accounts.add(obj.Acct__c);
}
list<Account> acctlist = [select Name from account where Name in :Accounts];
if (acctlist.size() > 0 ){
for (Integer i = 0; i < Trigger.new.size(); i++)
{
if (Trigger.new[i].Acct__c != null)
{
Trigger.new[i].Account__c = acctlist[i].ID;
}
else
{
Trigger.new[i].Account__c = null;
}
}
}
}
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.