I am trying to deploy a trigger to prod on salesforce. I was hoping someone could help me with an example of tests for this trigger.
Here is my trigger. It does its purpose, which is to update a bool field when a new contentNote (or anything of content type) that then has collateral effects through process builder.
trigger NewNote on ContentDocumentLink (before insert) {
Set<Id> setParentId = new Set<Id>();
List<Client_Relationships__c> crlst = new List<Client_Relationships__c>();
for (ContentDocumentLink cdl : trigger.new ) {
setParentId.add(cdl.LinkedEntityId);
}
crlst = [select Id , newNote__c from Client_Relationships__c where Id IN :setParentId];
For(Client_Relationships__c e : crlst)
{
e.newNote__c = True;
}
update crlst;
}
The trigger you wrote can be more efficient by omitting the SOQL query as seen below:
trigger NewNote on ContentDocumentLink (before insert) {
List<Client_Relationships__c> crlst = new List<Client_Relationships__c>();
for (ContentDocumentLink cdl : trigger.new ) {
if(cdl.LinkedEntityId.getSObjectType().getDescribe().getName() == 'Client_Relationships__c'){
crlst.add(
new Client_Relationships__c(
Id = cdl.LinkedEntityId,
newNote__c = true
)
);
}
}
update crlst;
}
The best practice would be to add your code to a handler or utility class and to only have one trigger per object. The name of this trigger could be changed to "ContentDocumentLinkTrigger" if you adopt that practice.
The test class for that trigger is below. I could not test the compilation because I don't have the same custom object.
#IsTest
private class ContentDocumentLinkTriggerTest {
#TestSetup
static void setupTest() {
insert new ContentVersion(
Title = 'Test_Document.txt',
VersionData = Blob.valueOf('This is my file body.'),
SharingPrivacy = 'N',
SharingOption = 'A',
Origin = 'H',
PathOnClient = '/Test_Document.txt'
);
List<Client_Relationships__c> relationships = new List<Client_Relationships__c>();
for(Integer i = 0; i < 300; i++){
relationships.add(
new Client_Relationships__c(
//add required field names and values
)
);
}
insert relationships;
}
static testMethod void testInsertTrigger() {
//prepare data
List<ContentVersion> contentVersions = new List<ContentVersion>([
SELECT Id, ContentDocumentId FROM ContentVersion
]);
System.assertNotEquals(0, contentVersions.size(), 'ContentVersion records should have been retrieved');
List<Client_Relationships__c> relationships = getAllClientRelationships();
System.assertNotEquals(0, relationships.size(), 'Client Relationship records should have been retrieved.');
List<ContentDocumentLink> documentLinks = new List<ContentDocumentLink>();
for(Integer i = 0; i < 252; i++){
documentLinks.add(
new ContentDocumentLink(
ContentDocumentId = contentVersions[0].ContentDocumentId,
LinkedEntityId = relationships[i].Id,
ShareType = 'I'
)
);
}
//test functionality
Test.startTest();
insert documentLinks;
Test.stopTest();
//assert expected results
List<Client_Relationships__c> relationshipsAfterProcessing = getAllClientRelationships();
for(Client_Relationships__c relationship : relationshipsAfterProcessing){
System.assert(relationship.newNote__c, 'The newNote__c field value should be true.');
}
}
private static List<Client_Relationships__c> getAllClientRelationships(){
return new List<Client_Relationships__c>([
SELECT Id, newNote__c FROM Client_Relationship__c
]);
}
}
For setting up test data, it is helpful to have a utility class that centralizes the creation of well-formed records. This is extremely useful when your code base gets large and a validation rule affects the insertion of new data in many test classes. With a centralized method, the inserted data only needs to be altered once.
Related
I have a challenge to complete. The requirement is automate record creation of a new Case record(renamed maintenance request). When an existing maintenance request(Case record) of type Repair or Routine Maintenance is closed, create a new maintenance request(Case record) for a future routine checkup. This new maintenance request is tied to the same Vehicle and Equipment(Product2- Standard Object renamed) Records as the original closed request. There is a logic on the due date field. It should have the min date compared to all the maintenance cycle of the equipment record related to the maintenance request.
I separated the logic in a helper class for the trigger. I believe the helper class code logic for 'Equipment Maintenance Item' record creation is not the right approach but trailhead already accepted it and provided me 500 points. Please help me find the right approach for the new 'Equipment Maintenance Item' record creation logic.
(The 'Equipment Maintenance Item' object is a Junction Object)
image
2 standard objects are used:
Maintenance Request (renamed Case) and Equipment (renamed Product)
2 custom objects are used:
Vehicle and Equipment Maintenance Item
Trigger:
trigger MaintenanceRequest on Case (before update, after update) {
if(Trigger.isAfter){
MaintenanceRequestHelper.updateWorkOrders(Trigger.New);
}}
Helper Class:
public with sharing class MaintenanceRequestHelper {
public static void updateWorkOrders(List<Case> CaseList) {
integer i=0;
List<Case> NewCaseList = new List<Case>();
List<Equipment_Maintenance_Item__c> eList = [SELECT Id,Maintenance_Request__c,Equipment__c,Quantity__c
FROM Equipment_Maintenance_Item__c
WHERE Maintenance_Request__c IN: CaseList];
List<Aggregateresult> mindue = new List<Aggregateresult>([SELECT MIN(Equipment__r.Maintenance_Cycle__c)MinimumValue
FROM Equipment_Maintenance_Item__c WHERE Maintenance_Request__c IN: CaseList]);
integer k=0;
List<Equipment_Maintenance_Item__c> newEmi = new List<Equipment_Maintenance_Item__c>();
for(Case c : CaseList){
if(c.Status == 'Closed' && (c.type =='Repair' || c.type =='Routine Maintenance')){
Case c1 = new Case(
Status = 'New',
Vehicle__c = c.Vehicle__c,
Type = 'Routine Maintenance',
Subject = 'Routine Checkup',
Date_Reported__c = Date.today(),
Product__c = c.Product__c,
AccountId = c.AccountId,
ContactId = c.ContactId,
Origin = c.Origin
//Date_Due__c = Date.today()
);
NewCaseList.add(c1);
for(Equipment_Maintenance_Item__c emi : eList){
if(c.Id == emi.Maintenance_Request__c){
newEmi.add(new Equipment_Maintenance_Item__c(
Equipment__c = emi.Equipment__c,
Maintenance_Request__c = c.Id,
Quantity__c = emi.Quantity__c));
}
}
for(Aggregateresult r:mindue){
if(r.get('MinimumValue')!=NULL){
NewCaseList[i].Date_Due__c=system.today()+integer.valueof(r.get('MinimumValue'));
}
i++;
}
}
}
if(NewCaseList.size()>0){
insert NewCaseList;
}
for(Case c2: NewCaseList){
for(Equipment_Maintenance_Item__c emi2 : newEmi){
emi2.Maintenance_Request__c = c2.id;
}
}
insert newEmi;
}
}
Your Code is correct but not bulkified. It will work for a single record and it will fail in case a bunch of records closed at the same time. I have solved it by creating one field on the Case object which will store cloned Case Id i.e. the old case from which the new case has been created.
Please refer code sample below:
trigger MaintenanceRequest on Case (before update, after update) {
//ToDo: Call MaintenanceRequestHelper.updateWorkOrders
if(trigger.isAfter){
MaintenanceRequestHelper.updateWorkOrders(trigger.new);
}
}
public with sharing class MaintenanceRequestHelper {
public static void updateWorkOrders(List<Case> newList) {
// TODO: Complete the method to update workorders
List<Case> CreateMaintReqLst = new List<Case>();
Set<Id> caseIdsSet = new Set<Id>();
List<Equipment_Maintenance_Item__c> newEMI = new List<Equipment_Maintenance_Item__c>();
for (Case caseRec : newList)
{
if(caseRec.Status == 'Closed' &&
(caseRec.Type =='Repair' || caseRec.Type =='Routine Maintenance'))
{
caseIdsSet.add(caseRec.Id);
}
}
List<Case> CaseList = [SELECT Id,Type,Status,Vehicle__c,Subject,Date_Reported__c,Date_Due__c,ProductId,Product__c,
(select Id,Maintenance_Cycle__c,Equipment__c,Quantity__c FROM Equipment_Maintenance_Items__r)
FROM Case
WHERE Id in:caseIdsSet];
for(Case caseRec:CaseList){
Integer minMaintCycle = 0;
List<Equipment_Maintenance_Item__c> EqpMaintList = caseRec.Equipment_Maintenance_Items__r;
if(EqpMaintList.size()>0){
for(Equipment_Maintenance_Item__c EquipMaint:EqpMaintList){
newEMI.add(new Equipment_Maintenance_Item__c(
Equipment__c = EquipMaint.Equipment__c,
Maintenance_Request__c = caseRec.Id,
Quantity__c = EquipMaint.Quantity__c));
if(Integer.valueOf(EquipMaint.Maintenance_Cycle__c) < minMaintCycle || minMaintCycle == 0){
minMaintCycle =Integer.valueOf(EquipMaint.Maintenance_Cycle__c);
}
}
}
Case newCase = new Case();
newCase.Type = 'Routine Maintenance';
newCase.Status = 'New';
newCase.Vehicle__c = caseRec.Vehicle__c;
newCase.Subject = String.isBlank(caseRec.Subject) ? 'Routine Maintenance Request' : caseRec.Subject;
newCase.Date_Reported__c = Date.today();
newCase.Date_Due__c = Date.today().addDays(minMaintCycle);
newCase.ProductId = caseRec.ProductId;
newCase.Product__c = caseRec.Product__c;
newCase.Cloned_Closed_Case_Id__c = caseRec.Id;
CreateMaintReqLst.add(newCase);
}
if(CreateMaintReqLst.size()>0){
Database.insert(CreateMaintReqLst);
}
for(Case c2: CreateMaintReqLst){
for(Equipment_Maintenance_Item__c emi2 : newEmi){
if(c2.Cloned_Closed_Case_Id__c == emi2.Maintenance_Request__c){
emi2.Maintenance_Request__c = c2.id;
}
}
}
if(newEmi.size()>0){
Database.insert(newEmi);
}
}
}
I have a Controller class on which I execute a SOQL query to Custom Metadata Type records.
The SOQL query also contains a child relationship query (Master-Detail relationship).
I need to write a test with mock custom metadata records
#AuraEnabled(cacheable=true)
public static List<Response> getLeadMetadataValues() {
List<Lead_Business_Status__mdt> leadBusinessStatusList = [
SELECT Id, Label, DeveloperName, Index__c,
(SELECT Id, Label, DeveloperName, Reason_Status_Api_Name__c FROM Lead_Reason_Status__r)
FROM Lead_Business_Status__mdt ORDER BY Index__c ASC
];
List<Response> resList = new List<Response>();
if (Test.isRunningTest()) {
leadBusinessStatusList = new List<Lead_Business_Status__mdt>();
leadBusinessStatusList.add(createMock());
}
for (Lead_Business_Status__mdt bs : leadBusinessStatusList) {
Response res = new Response();
res.Id = bs.Id;
res.businessStatusLabel = bs.Label;
res.businessStatusDevName = bs.DeveloperName;
res.index = bs.Index__c;
for (Lead_Reason_Status__mdt rs : bs.Lead_Reason_Status__r) {
ReasonStatus rsObj = new ReasonStatus();
rsObj.Id = rs.Id;
rsObj.reasonStatusLabel = rs.Label;
rsObj.reasonStatusDevName = rs.DeveloperName;
rsObj.fieldApiName = rs.Reason_Status_Api_Name__c;
res.reasonStatusList.add(rsObj);
}
resList.add(res);
}
return resList;
}
I use Test.isRunningTest() to populate the leadBusinessStatusList with the mock data.
I am able to create a mock object for the Master record: Lead_Business_Status__mdt and Detail record: Lead_Reason_Status__mdt. However, I wasn't able to add the Detail record to the related list: Lead_Reason_Status__r
private static Lead_Business_Status__mdt createMock() {
String reasonStatusStr = '{"Label":"Transfer to Queue", "DeveloperName":"Transfer_to_Queue", "Reason_Status_Api_Name__c":"Transfer_to_Queue"}';
Lead_Reason_Status__mdt reasonStatusObj = (Lead_Reason_Status__mdt) System.JSON.deserialize(reasonStatusStr, Lead_Reason_Status__mdt.class);
System.debug('Lead_Reason_Status__mdt: ' + reasonStatusObj);
String businessStatusStr = '{"Label":"Wrong Lead", "DeveloperName":"Wrong_Lead", "Index__c":"1"}';
Lead_Business_Status__mdt businessStatusObj = (Lead_Business_Status__mdt) System.JSON.deserialize(businessStatusStr, Lead_Business_Status__mdt.class);
return businessStatusObj;
}
The test is covered except the inner for loop of the Lead_Reason_Status__mdt.
How can I create a mock object with populating the child relationship list?
Can you try use JSON.deserialize method to setup these metadata records.
List<Lead_Business_Status__mdt> leadBusinessStatusList = (List< Lead_Business_Status__mdt >) JSON.deserialize( '[{"Id": "xoid914011599", "Label": "test", "DeveloperName": "test","Reason_Status_Api_Name__c": "test"}, {"Id": "xoid9140115992","Label":"test2","DeveloperName": "test2","Reason_Status_Api_Name__c": "test2"}]', List<Lead_Business_Status__mdt>.class );
Below is the trigger update a field on an attachment or file insert , can some one suggest a test class for this trigger?
Can some one please help in writing a #isTest class for below trigger to achieve 100 % code completion
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
if(trigger.isAfter) {
if(trigger.isInsert) {
ContentDocumentLinkTriggerHandler.onAfterInsert(trigger.new);
}
}
}
here is the class
public class ContentDocumentLinkTriggerHandler {
public static void onAfterInsert(list<ContentDocumentLink> lstCntLinks) {
set<Id> setTaskIds = new set<Id>();
list<task> lstTask=new list<task>();
Id recordTypeId = Schema.SObjectType.Task.getRecordTypeInfosByName().get('ERT_Task_Record_Type').getRecordTypeId();
for(ContentDocumentLink clIterator : lstCntLinks) {
setTaskIds.add(clIterator.LinkedEntityId);//Set all task id
}
if(!setTaskIds.isEmpty()) {
lstTask= [SELECT Id, Name,Attachment_Indicator__c FROM task WHERE Id IN :setTaskIds and recordtypeid=: recordTypeId ]; //Get all the task
}
for(task varTsk: lstTask){
varTsk.Attachment_Indicator__c=true;//Set Attachment Indicator Flag true
}
if(lstTask.size()>0){
update lstTask; //update task
}
}
}
Thanks in advance.
Carolyn
We aren't really a code writing service, you might want to hire some consultancy or even post the job on https://appexchange.salesforce.com/developers (it should be vetted by SF themselves, haven't used that part of appexchange personally). It's different if you have specific problem, wrote some code, got stuck with particular problem...
This should give you some ideas how it could be attacked. No promises it'll even compile. It's not pixel perfect, there's no negative test case (linking to task of different record type should not set the flag etc)
but it's a start.
#isTest
public with sharing class ContentDocumentLinkTriggerTest {
#isTest
public static void testSettingTaskFlag(){
Task t = new Task(
Subject = 'unit test here we go',
RecordTypeId = Schema.SObjectType.Task.getRecordTypeInfosByName().get('ERT_Task_Record_Type').getRecordTypeId()
);
insert t;
ContentVersion cv = new ContentVersion(
Title = 'Some document',
PathOnClient = 'some document.txt',
VersionData = Blob.valueOf('Lorem ipsum dolor sit amet...')
);
insert cv;
ContentDocument cd = [SELECT Id FROM ContentDocument WHERE LatestPublishedVersionId = :cv.Id];
ContentDocumentLink cdl = new ContentDocumentLink(
ContentDocumentId = cd.Id,
LinkedEntityId = t.Id
);
Test.startTest();
insert cdl;
Test.stopTest();
Task newTask = [SELECT Attachment_Indicator__c FROM Task WHERE Id = :t.Id];
System.assert(newTask.Attachment_Indicator__c);
}
}
I have a trigger that i have written that I need tested to be able to get code coverage in salesforce:
here is the trigger:
Trigger MyCh_SALESFORCETRIGGERUPDATE_tr_I on Account (after insert)
{
set<ID> ids = Trigger.newMap.keyset();
for(ID id : ids)
{
MyCh_Account__c change = new MyCh_Account__c();
change.Action__c = 'insert';
change.Link__c = id;
change.Processed__c = false;
change.Map__c = 'SALESFORCE TRIGGER UPDATE';
insert change;
}
}
I have tried :
#isTest
public class MyAccountcreationTest
{
static testMethod void testMethod1()
{
Account testAccount = new Account();
testAccount.Name='Test Account' ;
insert testAccount;
Account acc1 = [Select Id, Link__c, Action__c, Processed__c, Map__c from Account where Id =: testAccount.Id];
System.assertEquals(acc1.Name,'Test Account');
System.assertEquals(acc1.Link__c, acc1.Id);
System.assertEquals(acc1.Processed__c,false);
System.assertEquals(acc1.Map__c,'SALESFORCE TRIGGER UPDATE');
System.assertEquals(acc1.Action__c,'insert');
}
}
I expect the test to pass but it gives an error :
Map__c , Action__c , Link__c are not fields of Account object.
Also , how does the test actually link to the trigger itself ?
You are creating a MyCh_Account__c record in your trigger but you're not testing that in your test. You need to query for the record created in your trigger and assert that the fields are correct.
Im working on a MVC app.
When I call context.SaveChanges to update a specific records. The update is not registered in the database. I do not get any runtime error either. All in notice is that my Records is not updated. I still see the same values. Insert Functionality work Perfectly.
enter code here
public Admission Update(int stuid){
VDData.VidyaDaanEntities context = new VDData.VidyaDaanEntities();
VDData.Student_Master studentmaster = new VDData.Student_Master();
studentmaster.Student_ID = stuid;
studentmaster.Student_First_Name = this.FirstName;
studentmaster.Student_Middle_Name = this.MiddleName;
studentmaster.Student_Last_Name = this.LastName;
studentmaster.Student_Address_1 = this.Address;
studentmaster.Student_Address_2 = this.Address2;
studentmaster.Student_City = this.City;
studentmaster.Student_State = this.State;
studentmaster.Student_Pin_Code = this.Pincode;
context.SaveChanges(); // here it wont give any kind of error. it runs sucessfully. }
First get the entity you are going to update:
var entity = obj.GetEntity(id);
entity.col1 = "value";
context.SaveChanges(entity);
hope this will help.
It seems like you want to update, so your code should be
VDData.Student_Master studentmaster = context.Student_Masters.Single(p=>p.Student_ID == stuid);
And you should not change the Student_ID if it is the primary key.
public Admission Update(int stuid){
VDData.VidyaDaanEntities context = new VDData.VidyaDaanEntities();
//VDData.Student_Master studentmaster = new VDData.Student_Master();
//REPLACE WITH
VDData.Student_Master studentmaster = context.Student_Masters.Where(p=>p.Student_ID == stuid);
studentmaster.Student_ID = stuid;
studentmaster.Student_First_Name = this.FirstName;
studentmaster.Student_Middle_Name = this.MiddleName;
studentmaster.Student_Last_Name = this.LastName;
studentmaster.Student_Address_1 = this.Address;
studentmaster.Student_Address_2 = this.Address2;
studentmaster.Student_City = this.City;
studentmaster.Student_State = this.State;
studentmaster.Student_Pin_Code = this.Pincode;
context.SaveChanges();
Before
context.SaveChanges();
You need to call this
context.Student_Masters.Add(studentmaster );
Edit: introduce Abstraction to your Context class and Create a method in your context class like below, then you can call it whenever you want to create or update your objects.
public void SaveStudent_Master(Student_Master studentmaster)
{
using (var context = new VDData.VidyaDaanEntities())
{
if (studentmaster.Student_ID == 0)
{
context.Student_Masters.Add(studentmaster);
}
else if (studentmaster.Student_ID > 0)
{
//This Updates N-Level deep Object grapgh
//This is important for Updates
var currentStudent_Master = context.Student_Masters
.Single(s => s.Student_ID == studentmaster.Student_ID );
context.Entry(currentStudent_Master ).CurrentValues.SetValues(studentmaster);
}
context.SaveChanges();
}
Then in your Controller replace context.SaveChanges(); with _context.SaveStudent_Master(studentmaster);