You need to sign in to do that
Don't have an account?
Gayatri Karthik
Hi I m new to sales force development ,I wrote Apex trigger for cross objects and finally trigger working ,now I should move to production ,I tried to write test class ,code coverage was zero ,Kindly help me
Trigger code
trigger Ag_PurchaseContract_UpdateTradeConfirmation_Trigger on Ag_Purchase_Contract__c (after insert,after update) {
set<string> tradeConfirmationNumSet = new set<String>();
string purchaseContractName = null;
for (Ag_Purchase_Contract__c purchase : Trigger.new)
{
list<Ag_Purchase_Contract__c> purlist = [select id, Trade_Confirmation_No__c,name from Ag_Purchase_Contract__c where Trade_Confirmation_No__c = :purchase.Trade_Confirmation_No__c and name != :purchase.name];
if (purlist != null && purlist.size() > 0 && purchase.Trade_Confirmation_No__c != null)
{
purchase.adderror('The TradeNumber is used in a different Purchase contract. The purchase contract number is ' + purlist[0].name );
}
else
{
purchaseContractName = purchase.name;
if(purchase.Trade_Confirmation_No__c != null )
{
if (purchase.Trade_Confirmation_No__c == Trigger.NewMap.get(purchase.Id).Trade_Confirmation_No__c)
{
tradeConfirmationNumSet.add(Trigger.NewMap.get(purchase.Id).Trade_Confirmation_No__c);
}
List<Trade_Confirmation__c> tcList = [Select ID,purchase_contract__c,name from Trade_Confirmation__c where id IN :tradeConfirmationNumSet];
for (Trade_Confirmation__c tc: TClist)
{
tc.purchase_contract__c = purchaseContractName;
}
update tcList;
}
}
}
}
the above trigger is working and
test class is
@isTest
private class Test_Ag_PurchaseContract {
@istest static void testSuccess(){
set<string> tradeConfirmationNumSet = new set<String>();
string purchaseContractName = 'AGP/19/03/16599';
String tradeConfirmationnum = 'AGC0000012';
Ag_Purchase_Contract__c purchase = new Ag_Purchase_Contract__c();
list<Ag_Purchase_Contract__c> purlist = [select id, Trade_Confirmation_No__c,name from Ag_Purchase_Contract__c where Trade_Confirmation_No__c = :tradeConfirmationnum and name=:purchaseContractName limit 1];
if (purlist != null && purlist.size() > 0)
{
purchase.adderror('The TradeNumber is used in a different Purchase contract. The purchase contract number is ' + purlist[0].name );
}
else
{
if(purchase.Trade_Confirmation_No__c != null )
{
tradeConfirmationNumSet.add(tradeConfirmationnum);
}
List<Trade_Confirmation__c> tcList = [Select ID,purchase_contract__c,name from Trade_Confirmation__c where id IN :tradeConfirmationNumSet limit 1];
system.debug('purchaseContractName '+ purchase.name);
system.debug('purchaseContractName After assign '+ purchaseContractName);
system.debug('TClist :'+tcList.size());
if(TClist!=null && TClist.size()>0)
{
for (Trade_Confirmation__c tc: TClist)
{
tc.purchase_contract__c = purchaseContractName;
system.debug('tc.purchase_contract__c '+ tc.purchase_contract__c);
}
}
try
{
update tcList;
}
catch(Exception e)
{
system.assertEquals(e.getMessage(), e.getMessage()) ;
}
}
}
}
With this test class Code coverage is zero.how to write pls help me ,
trigger Ag_PurchaseContract_UpdateTradeConfirmation_Trigger on Ag_Purchase_Contract__c (after insert,after update) {
set<string> tradeConfirmationNumSet = new set<String>();
string purchaseContractName = null;
for (Ag_Purchase_Contract__c purchase : Trigger.new)
{
list<Ag_Purchase_Contract__c> purlist = [select id, Trade_Confirmation_No__c,name from Ag_Purchase_Contract__c where Trade_Confirmation_No__c = :purchase.Trade_Confirmation_No__c and name != :purchase.name];
if (purlist != null && purlist.size() > 0 && purchase.Trade_Confirmation_No__c != null)
{
purchase.adderror('The TradeNumber is used in a different Purchase contract. The purchase contract number is ' + purlist[0].name );
}
else
{
purchaseContractName = purchase.name;
if(purchase.Trade_Confirmation_No__c != null )
{
if (purchase.Trade_Confirmation_No__c == Trigger.NewMap.get(purchase.Id).Trade_Confirmation_No__c)
{
tradeConfirmationNumSet.add(Trigger.NewMap.get(purchase.Id).Trade_Confirmation_No__c);
}
List<Trade_Confirmation__c> tcList = [Select ID,purchase_contract__c,name from Trade_Confirmation__c where id IN :tradeConfirmationNumSet];
for (Trade_Confirmation__c tc: TClist)
{
tc.purchase_contract__c = purchaseContractName;
}
update tcList;
}
}
}
}
the above trigger is working and
test class is
@isTest
private class Test_Ag_PurchaseContract {
@istest static void testSuccess(){
set<string> tradeConfirmationNumSet = new set<String>();
string purchaseContractName = 'AGP/19/03/16599';
String tradeConfirmationnum = 'AGC0000012';
Ag_Purchase_Contract__c purchase = new Ag_Purchase_Contract__c();
list<Ag_Purchase_Contract__c> purlist = [select id, Trade_Confirmation_No__c,name from Ag_Purchase_Contract__c where Trade_Confirmation_No__c = :tradeConfirmationnum and name=:purchaseContractName limit 1];
if (purlist != null && purlist.size() > 0)
{
purchase.adderror('The TradeNumber is used in a different Purchase contract. The purchase contract number is ' + purlist[0].name );
}
else
{
if(purchase.Trade_Confirmation_No__c != null )
{
tradeConfirmationNumSet.add(tradeConfirmationnum);
}
List<Trade_Confirmation__c> tcList = [Select ID,purchase_contract__c,name from Trade_Confirmation__c where id IN :tradeConfirmationNumSet limit 1];
system.debug('purchaseContractName '+ purchase.name);
system.debug('purchaseContractName After assign '+ purchaseContractName);
system.debug('TClist :'+tcList.size());
if(TClist!=null && TClist.size()>0)
{
for (Trade_Confirmation__c tc: TClist)
{
tc.purchase_contract__c = purchaseContractName;
system.debug('tc.purchase_contract__c '+ tc.purchase_contract__c);
}
}
try
{
update tcList;
}
catch(Exception e)
{
system.assertEquals(e.getMessage(), e.getMessage()) ;
}
}
}
}
With this test class Code coverage is zero.how to write pls help me ,
Apologies - busy day at work as I wrap up this contract.
@apuroop
that looks much better - i wil cast an eye over it shortly.
Here is my version of a solution -
first a trigger Handler - I like handlers as the make the trigger easy to read, and allow full control over how things are executed. My handler is also quite structured. Note that we have no SELECT statements in FOR loops. And we have methods that clearly perform one action (or a limited number of actions ) . Whilst it makes the Handler longer, it provides clarity around what is happening where. In the Handler above, we have two clear methods - one to ensure we don't duplicate the TradeConfirmation and one to update the TradeConfirmation. The other reason to split them is that a validation method will occur in the Before Trigger and updating of other records will happen in the After Trigger.
The heavy lifting is done in the methods. By having the handler call a method which calls another method it allows clarity of what is happening in the Handler: Example only (not part of solution) Where it makes sense to group actions in a method, then do so. And by having the methods doing the work, we write the logic in one place but use in in multipe areas - example above , we call checkTradeNumber from both the before insert and before update.
Next we have the simple trigger structure which calls the Handler. This is fairly standard trigger structure for me (except here I have not included the delete call)
Yes, we can nest the IF statements like below: - personal preference - i use either
Now for the test class.
Test class should test all the methods that are called or expected to action.
So we test:
1. Successful insert
2. Successful update
3. Unsuccessful insert
4. Unsuccessful update
I run a separate test method for each item that I want to test. In that way, if a test fails, I get a test method name which helps narrow the issue - if the other tests are good, and only one test fails, its just one test to fix / investigate.
I hope the above proves beneficial.
Regards
Andrew
All Answers
You are missing an insert statement in your test class. You did a great job being totally new to Apex.
Try this:
OMG ... as a test class, just Oh my goodness
A test class should not simply repeat the class it is testing, it should be testing that the test class performs the expected action / outcome.
And then to assert with system.assertEquals(e.getMessage(), e.getMessage())
But before the test class, why don't we actually point out the SELECT statement inside a FOR loop.
@Gayatri
give me a little time and I will do a proper solution with explanation.
Regards
Andrew
Of course that's the way to test. I was just pointing out why the test class isn't covering at all. I guess then the trigger needs to be modified too, there's a DML statement inside a FOR loop.
I'm trying to read your code to determine your data structure and your intent of the trigger.
Structure
Object:
Ag_Purchase_Contract__c
Has fields:
Name - text field
Trade_Confirmation_No__c - lookup field to object Trade_Confirmation__c
Object:
Trade_Confirmation__c
Has fields:
purchase_contract__c - text field.
Trigger intent:
1. A Trade Confirmation Number can only be used in one Purchase Contract.
2. If the Trade Confirmation Number is not used elsewhere, update the Trade Confirmation record with the PurchaseContractName (Ag_Purchase_Contract__c.Name)
Can you confirm that the above is correct?
Regards
Andrew
Thank you below information with few modifications
I'm trying to read your code to determine your data structure and your intent of the trigger.
Structure
Object:
Ag_Purchase_Contract__c
Has fields:
Name -
text field<Auto generation number>Trade_Confirmation_No__c - lookup field to object Trade_Confirmation__c
Object:
Trade_Confirmation__c
Has fields:
purchase_contract__c - text field.
Trigger intent:
1. A Trade Confirmation Number can only be used in one Purchase Contract. < Yes, correct>
2. If the Trade Confirmation Number is not used elsewhere, update the Trade Confirmation record with the PurchaseContractName (Ag_Purchase_Contract__c.Name)
<If the trade confirmation number is selected in purchase contract object, update the trade confirmation record with the purchasecontractname>
Regards
Gayatri
Shoot in the dark.. Is the trigger active? Yes
Also, like Andrew suggested, you might want to add some more assert statements. I did my best! :)
TRIGGER:
TEST CLASS:
Apologies - busy day at work as I wrap up this contract.
@apuroop
that looks much better - i wil cast an eye over it shortly.
Here is my version of a solution -
first a trigger Handler - I like handlers as the make the trigger easy to read, and allow full control over how things are executed. My handler is also quite structured. Note that we have no SELECT statements in FOR loops. And we have methods that clearly perform one action (or a limited number of actions ) . Whilst it makes the Handler longer, it provides clarity around what is happening where. In the Handler above, we have two clear methods - one to ensure we don't duplicate the TradeConfirmation and one to update the TradeConfirmation. The other reason to split them is that a validation method will occur in the Before Trigger and updating of other records will happen in the After Trigger.
The heavy lifting is done in the methods. By having the handler call a method which calls another method it allows clarity of what is happening in the Handler: Example only (not part of solution) Where it makes sense to group actions in a method, then do so. And by having the methods doing the work, we write the logic in one place but use in in multipe areas - example above , we call checkTradeNumber from both the before insert and before update.
Next we have the simple trigger structure which calls the Handler. This is fairly standard trigger structure for me (except here I have not included the delete call)
Yes, we can nest the IF statements like below: - personal preference - i use either
Now for the test class.
Test class should test all the methods that are called or expected to action.
So we test:
1. Successful insert
2. Successful update
3. Unsuccessful insert
4. Unsuccessful update
I run a separate test method for each item that I want to test. In that way, if a test fails, I get a test method name which helps narrow the issue - if the other tests are good, and only one test fails, its just one test to fix / investigate.
I hope the above proves beneficial.
Regards
Andrew
Your trigger is better, but as you read it, you can see that you are duplicating code. Your if statment for trigger.isNew / trigger.isUpdate. if you created a method, you would only need to write the code once, but call the method twice.
Your test method is better. However, I steer away from constants in my test classes.
he other thing to consider is that when you insert a record, the values that are being updated by the trigger will not exist as you insert. So you would need to query the records again, including the field to be test and then run your assert on the returned field.
Asserts like:
System.assertEquals('AGC0000012', tradeConfirmationnum.Name);
wont actually have a lot of value as you haven't really confirmed that the record has been written to the server. If you want to be precise, you are simply testing that you could write to that field. If there was some validation around the field, then there could be value, but not for this test case.
It is better to insert / update the record and then requery the record including the field and then doing the assert.
And you should also do both positive and negative tests. In the above, yes we were succesful in inserting and then we also test where there was a conflict, and as we are throwing an exception, try and catch the exception and assert that the exception was expected.
Hope that is helpful
Regards
Andrew
The thing that you told about querying a record after insert for testing - I agree what you're saying but in my test class, lines like 10, 11, 12, 17, 18 work fine which means they're returning what we're expecting, correct? Are you saying it's not the ideal way of testing?
Thanks for the detailed explanation. :)
Well, if we think about why we write test code - it is to ensure the code we write does what it is supposed to do ... and to ensure what we write doesn't break the SF platform (why we bulkify).
So if we look at a snippet of your code (numbers included to assist explanation)
Fairly straightforward - create a record, insert and then test.
But what are we testing? that the record was saved as expected. Well, guess what? That is what Salesforce promises it will do - that if i put an entry in a field and hit save, the record will keep the value. So testing the individual field values only checks that Salesforce did it's job. It doesn't really test the custom code.
Now, the other element to consider is how the code works. When I declare creation of an record (tradeConfirmationnum ) , that record , and the values i assign, is held in memory. So when we do an assert against that record, I am testing the "in memory" record, not the record that is written to the database. So, if my code is doing something as I write the record to the database, I should grab that written record and test that the values that I expect my code to write have occurred.
One of the vagaries of test code is that the ID is available after insert. It is an interesting question. It would seem to indicate that the "insert" of the "in memory" record does update the "in memory" record. I usually, even if doing a before insert trigger test, grab the record from the database with a query after the insert. I might go play and see if a "before insert" trigger will update a non-defined field "in memory".
I hope my explanation is satisfactory.
Regards
Andrew
Sorry for stealing your thread, but hopefully you have found this educational.
@apuroop
Ok, here is a quick example of why we grab the written record in our Apex Test Classes
Simple Before Insert Trigger: And a test class
Test one:
So, we notice that in the first test the Industry value is null when we assert against 'in memory' value.
But when we grab the written record, the asserted value is Other. (as set by the trigger).
Note that I also have an assert before we insert the newLead record, to show that the record Id has not been created before we do the insert.
Now, test two:
To check that the issue is not because I didn't declare the field in the record creation, when we set the value to '' (blank) that is what is asserted in the in memory record. System.assertEquals('',beforeLead.Industry); This is because it is "in memory".
But when we grab the written record, the asserted value is Other. (as set by the trigger).
I included the Name field, as out of the box, the Name field is a concatenation of the FirstName and LastName fields. So even for OoB SF functions, the 'in memory' record is not updated by the insert, but we see that the Id is available to the in memory record after the insert.
Regards
Andrew
First of all thank you for step by step explanation. Based on your code I modified my trigger and APEX Test class . NOw code coverage is 100% . As I am new to sales force development above mentioned article is very useful for my future Apex Classes development.Any doubts I will post the thread again.Thanks for your valuable time.
regards,
Gayatri
quick question : above scenario like cross object fields update Must use APEX Triggers or any other way we can handle in sales force?
Depending on the exact update and the relation to the first object, you could use Process Builder or Flows, or a combination of both. And if you want to simply display data in a child, you could use a formula field.
Regards
Andrew