I have a trigger in Apex. How do I write a unit test that checks if the trigger was called?
Account account = new Account(Name='Test account');
insert account;
checkIfInsertTriggerCalled(); // how do I implement this?
You should be testing what the trigger does, not just if it was called or not. What does your trigger do?
If you are just trying to see if it inserted then:
Account account = new Account(Name='Test account');
insert account;
List<Account> aList = [SELECT Id, Name FROM Account];
system.assertEquals(1,aList.size());
side note: I would have just left a comment, but I am a few rep short on this site.
edit: Here is the standard page that gets linked to a lot: https://developer.salesforce.com/page/How_to_Write_Good_Unit_Tests
Related
I have created a trigger in Salesforce :
Trigger myTestDelete_D on Account (before delete)
{
set<ID> ids = Trigger.oldMap.keyset();
myClass.Details('Account','delete',ids);
}
So the steps are I create an account Acct1.
I then delete the account , but in the call to myClass.Details I perform a select to the account with the id of the Acct1.
The issue is that the row has already been deleted and hence I get 0 rows.
Is there some sort of setting that is required in Salesforce to wait until the trigger has completed before the entity is deleted ?
I can't give you more details because we don't have the code of your "myClass" class but the query should retrieve the Accounts in the before delete trigger execution. I've created myself a trigger and set up some logs. Here are the results after deleting one Account record.
Trigger Code:
trigger Account on Account(before delete) {
System.debug(Trigger.oldMap);
System.debug(Trigger.newMap);
List<Account> deletedAccounts = [
SELECT Id, Name
FROM Account
WHERE Id IN :Trigger.oldMap.keySet()
];
System.debug(deletedAccounts.size());
}
System.debug(Trigger.oldMap):
{0013O00000kVO2PQAW=Account:{Id=0013O00000kVO2PQAW, IsDeleted=false, MasterRecordId=null, Name=My Account, Type=null, ParentId=null, BillingStreet=null, BillingCity=null, BillingState=null, BillingPostalCode=null, BillingCountry=null, BillingLatitude=null, BillingLongitude=null, BillingGeocodeAccuracy=null, ShippingStreet=null, ShippingCity=null, ShippingState=null, ShippingPostalCode=null, ShippingCountry=null, ShippingLatitude=null, ShippingLongitude=null, ShippingGeocodeAccuracy=null, Phone=null, Fax=null, AccountNumber=null, Website=null, PhotoUrl=null, Sic=null, Industry=null, AnnualRevenue=null, NumberOfEmployees=null, Ownership=null, TickerSymbol=null, Description=null, Rating=null, Site=null, OwnerId=0053O0000044sSdQAI, CreatedDate=2021-10-05 16:47:55, CreatedById=0053O0000044sSdQAI, LastModifiedDate=2021-10-05 16:47:55, LastModifiedById=0053O0000044sSdQAI, SystemModstamp=2021-10-05 16:47:55, LastActivityDate=null, LastViewedDate=null, LastReferencedDate=null, Jigsaw=null, JigsawCompanyId=null, CleanStatus=Pending, AccountSource=null, DunsNumber=null, Tradestyle=null, NaicsCode=null, NaicsDesc=null, YearStarted=null, SicDesc=null, DandbCompanyId=null, OperatingHoursId=null}}
System.debug(Trigger.newMap):
null
System.debug(deletedAccounts.size()):
1
So, I would suggest that the problem is probably in the class that is doing the Query as the records are still available for querying in the before delete trigger.
Im trying to create automation to copy Zipcode__c text field in connection sObject to Zip_code__c text field on Prem sObject. I can't use formula references since i need to be able to search copied field. One connection can have many Prems.
trigger updatePremFromConnection on Prem__c (before insert,after insert, after update,before update) {
List<Connection__c> connection = new List<Connection__c>();
for (Prem__c p: [SELECT Connection_id__c,id, Name
FROM Prem__c
WHERE Connection_id__c
NOT IN (SELECT id FROM Connection__c)
AND id IN : Trigger.new ]){
connection.add(new Connection__c(
ZipCode__c = p.Zip_Code__c));
}
if (connection.size() > 0) {
insert connection;
}
}
i need ZIp code field on the prem__c to be auto updated when i edit connection.
There are several issues with this code.
Trigger Object
Your trigger is on the wrong object and is doing exactly the opposite of your stated intent.
i need ZIp code field on the prem__c to be auto updated when i edit connection.
Your trigger on the Prem__c object attempts to copy data to the Connection__c object, while your objective is to copy from Prem__c to Connection__c. You'll definitely need an after update trigger on Connection__c and a before insert trigger on Prem__c; however, if the relationship between the two objects is Lookup or a Master-Detail relationship configured to be reparentable, you'll also need an update trigger on the child object Prem__c to handle situations where the child record is reparented, by updating from the new parent Connection.
Logic
This logic:
for (Prem__c p: [SELECT Connection_id__c,id, Name
FROM Prem__c
WHERE Connection_id__c
NOT IN (SELECT id FROM Connection__c)
AND id IN : Trigger.new ]){
connection.add(new Connection__c(
ZipCode__c = p.Zip_Code__c));
}
really doesn't make sense. It only finds Prem__c records in the trigger set that don't have an associated Connection, makes a new Connection, and then doesn't establish a relationship between the two records. The way that it does this is needlessly inefficient; that NOT IN subquery doesn't need to be there because it can simply by Connection_Id__c = null.
Instead, you probably want your Connection__c trigger to have a query like this:
SELECT ZipCode__c, (SELECT Zip_Code__c FROM Prems__r)
FROM Connection__c
WHERE Id IN :Trigger.new
Then, you can iterate over those Connection__c records with an inner for loop over their associated Prem__c records. Note that above you'll need to use the actual relationship name where I have Prems__r. The logic would look something like this:
for (Connection__c conn : queriedConnections) {
for (Prem__c prem : conn.Prems__r) {
if (prem.Zip_Code__c != conn.ZipCode__c) {
prem.Zip_Code__c = conn.ZipCode__c
premsToUpdate.add(prem);
}
}
}
update premsToUpdate;
Before running the query, you should also gather a Set<Id> of only those records for which the ZipCode__c field has actually changed, i.e., where thisConn.ZipCode__c != Trigger.oldMap.get(thisConn.Id).ZipCode__c. You'd use that Set<Id> in place of Trigger.new in your query, so that you only obtain those records with relevant changes.
This is the challenge:
To complete this challenge, you need to add a trigger for Opportunity. The trigger will add a task to any opportunity inserted or updated with the stage of 'Closed Won'. The task's subject must be 'Follow Up Test Task'.
The Apex trigger must be called 'ClosedOpportunityTrigger'
With 'ClosedOpportunityTrigger' active, if an opportunity is inserted or updated with a stage of 'Closed Won', it will have a task created with the subject 'Follow Up Test Task'.
To associate the task with the opportunity, fill the 'WhatId' field with the opportunity ID.
This challenge specifically tests 200 records in one operation.
Here is my Code
trigger ClosedOpportunityTrigger on Opportunity (before insert, before update) {
List<Opportunity> opportunities = [SELECT Id, StageName
FROM Opportunity
WHERE Id
IN :Trigger.New];
List<Task> tasksToUpdate = new List<Task>();
System.debug('##### OPS' + opportunities);
for(Opportunity o : opportunities){
System.debug('##### ' + o.StageName);
if(o.StageName == 'Closed Won'){
Task thisTask = new Task(WhatId = o.Id, Subject = 'Follow Up Test Task');
tasksToUpdate.add(thisTask);
System.debug('##### ' + tasksToUpdate);
}
}
insert tasksToUpdate;
}
When I try to validate through trailhead, it gives a "Challenge Not yet complete... here's what's wrong:
Executing against the trigger does not work as expected." error.
I added some debug print and it seems to show that the soql statement just does not pull any results, so it does not enter the if statement. It seems a pretty straightforward soql statement to me, but I must be missing something. This happens no matter if I add or update an item.
Thanks in advance
In a Trigger context you do not need to use a SOQL query to retrieve the records that are being inserted or updated.
Also, with a before insert trigger the records won't be in the database yet, so they won't have any Id's defined. That's why the query isn't returning anything.
Instead you will want to use the Trigger Context Variables to work the the records.
In particular, you can use Trigger.new to get a collection of records being inserted or updated. Try looping over this collection instead of using a SOQL query in the trigger.
trigger ClosedOpportunityTrigger on Opportunity (after insert, after update) {
List<Task> taskListToInsert = new List<Task>();
for(Opportunity opp : [Select Id,StageName from Opportunity
where Id in :Trigger.New AND StageName = 'Closed Won'])
{
taskListtoInsert.add(new Task(Subject ='Follow Up Test Task',WhatId = opp.Id));
}
if(taskListtoInsert.size()>0)
{
insert taskListtoInsert;
}
}
The problem with your code is that you are running on before insert/update where the element has no ID yet. Therefore the following code:
Task thisTask = new Task(WhatId = o.Id, Subject = 'Follow Up Test Task');
Insert a task with an empty WhatId which doesn't pass the test. Just change to after insert/after update the trigger event.
All right, I was having trouble with this challenge because of the Task things, seems it's just a default object from salesforce!
Your problem is that you don't bulk enough your code as they shown in the trail.
trigger ClosedOpportunityTrigger on Opportunity (after insert, after update) {
List<Opportunity> closedOpp = [Select id from opportunity
where id IN :Trigger.New
AND StageName = 'Closed Won'];
List<Task> triggeredTasks = new List<Task>();
for (Opportunity o : closedOpp){
Task tache = new Task (Subject = 'Follow Up Test Task',
WhatId = o.ID);
triggeredTasks.add(tache);
}
insert triggeredTasks;
}
Here's code that worked:
trigger ClosedOpportunityTrigger on Opportunity (after insert, after update) {
List<Task> OpTasklist = new List<Task>();
// Iterate over opportunities that are in this trigger and have a stage of "Closed Won"
for (Opportunity op: [SELECT id FROM Opportunity
WHERE Id IN :Trigger.New AND
StageName =: 'Closed Won']) {
if ((Trigger.IsUpdate && Trigger.oldMap.get(op.Id).StageName != 'Closed Won')) OR
(Trigger.IsInsert) {
OpTaskList.add(new Task (Subject='Follow Up Test Task',
WhatId = op.Id)); }
}
If (OpTaskList.size() > 0) {
Insert OpTaskList ;
}
}
trigger ClosedOpportunityTrigger on Opportunity (after insert,after update) {
List oppty = [Select id from opportunity where id IN :Trigger.New AND StageName = 'Closed Won'];
List<Task> new_task = new List<Task>();
for (Opportunity opp : oppty){
Task thistask = new Task (Subject = 'Follow Up Test Task',WhatId = opp.ID);
new_task.add(thistask);
}
insert new_task;
}
We have two standard objects account(parent) and contact (child ). i want to write a trigger to populate the lastname field of contact with the name field of account
The trigger below performs the same task but soql query is from child to parent .
I want the trigger which performs the same work but using soql query ( relationship query ) from parent to child .
trigger trgSetLastName on Contact (after insert)
{
List<Contact> lstConUpdate = new List<Contact>();
List<Contact> lstContact = [select id,Account.Name from Contact where
id in: trigger.newmap.keyset()];
for(Contact con: lstContact)
{
con.LastName = con.Account.Name;
lstConUpdate.add(con);
}
if(lstConUpdate.size() > 0){
update lstConUpdate;
}
}
i want a trigger for this .help
Step 1.
Build a Set<id> of Account Ids:
for (Contact c : trigger.new){
acctIdSet.add(c.AccountId);
}
Step 2.
Select related Accounts into a Map<Id,Account> using:
[SELECT {fields}
FROM Account
WHERE Id IN :acctIdSet];
Step 3.
Loop through trigger.new and extract the corresponding Account from your Map.
Step 4.
Update the LastName value on contact with the value on your Account - assuming it exists.
Step 5.
Make your trigger fire before insert:
trigger trgSetLastName on Contact (before insert, before update)
I want to update a record if the record exists or insert a new one if it doesn't.
What would be the best approach?
Do a Select Count() and if comes back zero then insert, if one then query the record, modify and update,
or should I just try to query the record and catch any system.queryexception?
This is all done in Apex, not from REST or the JS API.
Adding to what's already been said here, you want to use FOR UPDATE in these cases to avoid what superfell is referring to. So,
Account theAccount;
Account[] accounts = [SELECT Id FROM Account WHERE Name = 'TEST' LIMIT 1 FOR UPDATE];
if(accounts.size() == 1)
theAccount = accounts[0];
else
theAccount = new Account();
// Make modifications to theAccount, which is either:
// 1. A record-locked account that was selected OR
// 2. A new account that was just created with new Account()
upsert theAccount;
You should use the upsert call if at all possible, the select then insert/update approach is problematic once you get into the realm of concurrent calls unless you goto the trouble of correctly locking a parent row as part of the select call.
I would try it with a list and isEmpty() function:
List<Account> a = [select id from account where name = 'blaahhhh' Limit 1];
if(a.isEmpty()){
System.debug('#### do insert');
}
else{
System.debug('#### do update');
}