+ Start a Discussion
DiegoT.DiegoT. 

Test class for trigger update relationship on Contact object

Hi everyone, i am new with Apex and i have a question.
I want to update the Contact object field location_boat__c and that the location__c fields of the Boat object be updated also be updated My test is incompleted. i want test my trigger UpdateLocationand i dont know how do it
the object Boat field name Contac__c have relationship lookup with the Contact (Object standard)
My test is wrong but i can not how resolve this problem
trigger UpdateLocation on Contact (after update) { if (trigger.isAfter
&& Trigger.isUpdate) {
     List<Boat__c> boats = new List<Boat__c>();
     for (Contact c : [
                 SELECT location_boat__c, (SELECT Id FROM Boats__r)
                 FROM Contact
                 WHERE Id = :trigger.new
             ]) {
         for (Boat__c b : c.Boats__r) {
             b.location__c = c.location_boat__c;
             boats.add(b);
         }
     }
     update(boats); }}
 
@isTest
public class PruebaBotes {
@isTest static void TestActualizar() {

            Boat__c b1  = new Boat__c(Name='bote uno' ,Price__c=20, Type__c='Velero', location__c='Argentina');
            Boat__c b2  = new Boat__c(Name='bote dos' ,Price__c=50, Type__c='Yate', location__c='Brasil');
            Boat__c b3  = new Boat__c(Name='bote tres' ,Price__c=125, Type__c='Catamaran', location__c='España');
            Boat__c b4  = new Boat__c(Name='bote cuatro' ,Price__c=700, Type__c='Velero', location__c='India');

            insert b1;
            insert b2;
            insert b3;
            insert b4;   

            Contact a1 = new Contact(Lastname='Pepe',  location_boat__c = 'Brasil');
            Contact a2 = new Contact(Lastname='Jose',  location_boat__c = 'Argentina');
            Contact a3 = new Contact(Lastname='Maria', location_boat__c = 'India');
            Contact a4 = new Contact(Lastname='Ana',   location_boat__c = 'Argentina');

            insert a1;
            insert a2;
            insert a3;
            insert a4;    

            Contact updateLocation;
            updateLocation = [SELECT location_boat__c FROM Contact WHERE location_boat__c = 'Argentina' LIMIT 1];
            updateLocation.location_boat__c = 'India';
            update updateLocation;

Test.startTest();
    Contact afterUpdate = [SELECT  location_boat__c FROM Contact WHERE 
    Id=:updateLocation.Id];
    System.assertEquals('India', afterUpdate.location_boat__c);

    Test.stopTest();


    }
    }

 
Best Answer chosen by DiegoT.
Nayana KNayana K
Please notice the first line :( you have written before insert. 
It should be after update (As per the code you have shared in the question).

please change it

All Answers

Nayana KNayana K
Hi DiegoT,

Always trigger should be bulkified (WHERE Id IN :trigger.new) It should not be written in single record context (WHERE Id = :trigger.new)

I have modified the code. Please have a look.
 
trigger UpdateLocation on Contact (after update) { 
	if(trigger.isAfter && Trigger.isUpdate) {
		 List<Boat__c> lstBoatToUpdate = new List<Boat__c>();
		 Map<Id, String> mapConIdToLocation = new Map<Id, String>();
		 String temp;
		 
		 for(Contact objContact : Trigger.New)
		 {
			/* if location is changed, then only update the relevant boats. Unnecessarily don't update the boats*/
			if(Trigger.oldMap.get(objContact.Id).location_boat__c != objContact.location_boat__c) {
				mapConIdToLocation.put(objContact.Id, objContact.location_boat__c);
			}
		 }
		 // don't do unncessarily query. 
		 if(!mapConIdToLocation.isEmpty()) {
			
			for (Boat__c objBoat : [SELECT location__c, Contact__c FROM Boat__c WHERE Contact__c IN: mapConIdToLocation.keySet()]) {
				 temp = mapConIdToLocation.get(objBoat.Contact__c);
				 /* If location of boat is not matching with contact.location_boat__c, then only update the boat*/
				 if(objBoat.location__c != temp) {
					objBoat.location__c = temp;
					lstBoatToUpdate.add(objBoat);
				 }
			 }
             // update only if we have records in boat list
			 if(!lstBoatToUpdate.isEmpty()) {
				update lstBoatToUpdate;
			 }
		 }
	 }
}
 
@isTest
public class PruebaBotes {
	@isTest static void TestActualizar() {
		List<Boat__c> lstBoat = new List<Boat__c>();
		Contact objContact = new Contact(Lastname='Pepe', location_boat__c = 'Brasil');
		insert objContact;
		
		// boats belong to objContact (Brasil)
		lstBoat.add(new Boat__c(Name='bote uno' ,Price__c=20, Type__c='Velero', location__c='Argentina', Contact__c = objContact.Id));
		lstBoat.add(new Boat__c(Name='bote dos' ,Price__c=50, Type__c='Yate', location__c='Brasil', Contact__c = objContact.Id));
		lstBoat.add(new Boat__c(Name='bote tres' ,Price__c=125, Type__c='Catamaran', location__c='España', Contact__c = objContact.Id));
		lstBoat.add(new Boat__c(Name='bote cuatro' ,Price__c=700, Type__c='Velero', location__c='India', Contact__c = objContact.Id));
		insert lstBoat;
		
		Test.startTest();
		objContact.location_boat__c = 'India';
		update objContact;
		Test.stopTest();
		
		System.assertEquals(lstBoat.size(), [SELECT COUNT() FROM Boat__c WHERE Contact__c =: objContact.Id AND location__c = 'India']);

    }
}

 
DiegoT.DiegoT.
Hi Nayana result test is, what is wrong ?
System.AssertException: Assertion Failed: Expected: 4, Actual: 1

 
Nayana KNayana K
Not sure.  Please post debug log of test execution 
DiegoT.DiegoT.
The problem is in line 20  System.assertEquals(lstBoat.size(), [SELECT COUNT() FROM Boat__c WHERE Contact__c =: objContact.Id AND location__c = 'India']);
size.list = 4 and the query select count = 1
i wanna see if the location__c of the Account is the same as the boat_location__c of object Boat after update but i dont know as do the test of trigger.
Nayana KNayana K
Yes I understood. But,  ideally query should return 4 .
That's why want to examine debug log to check if something else is going on between the flow. 
DiegoT.DiegoT.
Nayana Here my log console, please help me :(
ubicacion__c = location__c
ubicacion_bote__c = boat_location, sorry i have in spanish.
 
15:53:10.0 (508332)|EXECUTION_STARTED
15:53:10.0 (516082)|CODE_UNIT_STARTED|[EXTERNAL]|01p6g0000090rOQ|BotesTestUnitarios.TestActualizar()
15:53:10.0 (746944)|HEAP_ALLOCATE|[79]|Bytes:3
15:53:10.0 (781682)|HEAP_ALLOCATE|[84]|Bytes:152
15:53:10.0 (797058)|HEAP_ALLOCATE|[399]|Bytes:408
15:53:10.0 (811558)|HEAP_ALLOCATE|[412]|Bytes:408
15:53:10.0 (824457)|HEAP_ALLOCATE|[520]|Bytes:48
15:53:10.0 (848329)|HEAP_ALLOCATE|[139]|Bytes:6
15:53:10.0 (862268)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:35
15:53:10.0 (875381)|METHOD_ENTRY|[2]|01p6g0000090rOQ|BotesTestUnitarios.BotesTestUnitarios()
15:53:10.0 (881632)|STATEMENT_EXECUTE|[2]
15:53:10.0 (887723)|STATEMENT_EXECUTE|[2]
15:53:10.0 (892202)|METHOD_EXIT|[2]|BotesTestUnitarios
15:53:10.0 (923030)|HEAP_ALLOCATE|[52]|Bytes:5
15:53:10.0 (943810)|HEAP_ALLOCATE|[58]|Bytes:5
15:53:10.0 (952154)|HEAP_ALLOCATE|[66]|Bytes:7
15:53:10.0 (982809)|STATEMENT_EXECUTE|[3]
15:53:10.0 (985515)|STATEMENT_EXECUTE|[5]
15:53:10.0 (1069629)|HEAP_ALLOCATE|[5]|Bytes:4
15:53:10.0 (1162073)|VARIABLE_SCOPE_BEGIN|[5]|lstBoat|List<Bote__c>|true|false
15:53:10.0 (1176164)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (1217893)|VARIABLE_ASSIGNMENT|[5]|lstBoat|[]|0x632b232b
15:53:10.0 (1224382)|STATEMENT_EXECUTE|[6]
15:53:10.0 (1258114)|HEAP_ALLOCATE|[6]|Bytes:4
15:53:10.0 (1329777)|HEAP_ALLOCATE|[6]|Bytes:4
15:53:10.0 (1378224)|VARIABLE_ASSIGNMENT|[6]|this.LastName|"Pepe"|0x50112970
15:53:10.0 (1385140)|HEAP_ALLOCATE|[6]|Bytes:6
15:53:10.0 (1406190)|VARIABLE_ASSIGNMENT|[6]|this.Ubicacion_Bote__c|"Brasil"|0x50112970
15:53:10.0 (1430909)|VARIABLE_SCOPE_BEGIN|[6]|objContact|Contact|true|false
15:53:10.0 (1460938)|VARIABLE_ASSIGNMENT|[6]|objContact|{"LastName":"Pepe","Ubicacion_Bote__c":"Brasil"}|0x50112970
15:53:10.0 (1469358)|STATEMENT_EXECUTE|[8]
15:53:10.0 (1493501)|HEAP_ALLOCATE|[8]|Bytes:8
15:53:10.0 (1507660)|DML_BEGIN|[8]|Op:Insert|Type:Contact|Rows:1
15:53:10.0 (1539692)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:8
15:53:10.0 (12059099)|CODE_UNIT_STARTED|[EXTERNAL]|01q6g000000d7Pb|UpdateLocation on Contact trigger event BeforeInsert|__sfdc_trigger/UpdateLocation
15:53:10.0 (12120182)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:8
15:53:10.0 (12235672)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (12252297)|VARIABLE_SCOPE_BEGIN|[1]|this|UpdateLocation|true|false
15:53:10.0 (12295864)|VARIABLE_ASSIGNMENT|[1]|this|{}|0x67315e1
15:53:10.0 (12335614)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (12537408)|VARIABLE_SCOPE_BEGIN|[1]|this|UpdateLocation|true|false
15:53:10.0 (12553284)|VARIABLE_ASSIGNMENT|[1]|this|{}|0x67315e1
15:53:10.0 (12560710)|STATEMENT_EXECUTE|[1]
15:53:10.0 (12586776)|STATEMENT_EXECUTE|[2]
15:53:10.12 (12590670)|CUMULATIVE_LIMIT_USAGE
15:53:10.12 (12590670)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 1 out of 150
  Number of DML rows: 1 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

15:53:10.12 (12590670)|CUMULATIVE_LIMIT_USAGE_END

15:53:10.0 (13672219)|CODE_UNIT_FINISHED|UpdateLocation on Contact trigger event BeforeInsert|__sfdc_trigger/UpdateLocation
15:53:10.0 (43888980)|DML_END|[8]
15:53:10.0 (43920388)|STATEMENT_EXECUTE|[11]
15:53:10.0 (43958944)|HEAP_ALLOCATE|[11]|Bytes:4
15:53:10.0 (44060033)|HEAP_ALLOCATE|[11]|Bytes:8
15:53:10.0 (44124863)|VARIABLE_ASSIGNMENT|[11]|this.Name|"bote uno"|0x51a205c
15:53:10.0 (44163631)|HEAP_ALLOCATE|[11]|Bytes:28
15:53:10.0 (44192258)|VARIABLE_ASSIGNMENT|[11]|this.Precio__c|20|0x51a205c
15:53:10.0 (44199523)|HEAP_ALLOCATE|[11]|Bytes:6
15:53:10.0 (44217290)|VARIABLE_ASSIGNMENT|[11]|this.Tipo__c|"Velero"|0x51a205c
15:53:10.0 (44223674)|HEAP_ALLOCATE|[11]|Bytes:9
15:53:10.0 (44239061)|VARIABLE_ASSIGNMENT|[11]|this.Ubicacion__c|"Argentina"|0x51a205c
15:53:10.0 (44286839)|VARIABLE_ASSIGNMENT|[11]|this.Contacto__c|"0036g000003JGBQAA4"|0x51a205c
15:53:10.0 (44371925)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (44383854)|STATEMENT_EXECUTE|[12]
15:53:10.0 (44401795)|HEAP_ALLOCATE|[12]|Bytes:4
15:53:10.0 (44410109)|HEAP_ALLOCATE|[12]|Bytes:8
15:53:10.0 (44430534)|VARIABLE_ASSIGNMENT|[12]|this.Name|"bote dos"|0x13deeaf4
15:53:10.0 (44449252)|HEAP_ALLOCATE|[12]|Bytes:28
15:53:10.0 (44466487)|VARIABLE_ASSIGNMENT|[12]|this.Precio__c|50|0x13deeaf4
15:53:10.0 (44472585)|HEAP_ALLOCATE|[12]|Bytes:4
15:53:10.0 (44495306)|VARIABLE_ASSIGNMENT|[12]|this.Tipo__c|"Yate"|0x13deeaf4
15:53:10.0 (44512048)|VARIABLE_ASSIGNMENT|[12]|this.Ubicacion__c|"Brasil"|0x13deeaf4
15:53:10.0 (44534518)|VARIABLE_ASSIGNMENT|[12]|this.Contacto__c|"0036g000003JGBQAA4"|0x13deeaf4
15:53:10.0 (44566034)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (44574539)|STATEMENT_EXECUTE|[13]
15:53:10.0 (44586734)|HEAP_ALLOCATE|[13]|Bytes:4
15:53:10.0 (44593779)|HEAP_ALLOCATE|[13]|Bytes:9
15:53:10.0 (44611161)|VARIABLE_ASSIGNMENT|[13]|this.Name|"bote tres"|0xab081b0
15:53:10.0 (44621037)|HEAP_ALLOCATE|[13]|Bytes:28
15:53:10.0 (44635656)|VARIABLE_ASSIGNMENT|[13]|this.Precio__c|125|0xab081b0
15:53:10.0 (44641759)|HEAP_ALLOCATE|[13]|Bytes:9
15:53:10.0 (44655373)|VARIABLE_ASSIGNMENT|[13]|this.Tipo__c|"Catamaran"|0xab081b0
15:53:10.0 (44661293)|HEAP_ALLOCATE|[13]|Bytes:6
15:53:10.0 (44674571)|VARIABLE_ASSIGNMENT|[13]|this.Ubicacion__c|"España"|0xab081b0
15:53:10.0 (44695212)|VARIABLE_ASSIGNMENT|[13]|this.Contacto__c|"0036g000003JGBQAA4"|0xab081b0
15:53:10.0 (44720870)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (44728852)|STATEMENT_EXECUTE|[14]
15:53:10.0 (44739515)|HEAP_ALLOCATE|[14]|Bytes:4
15:53:10.0 (44746665)|HEAP_ALLOCATE|[14]|Bytes:11
15:53:10.0 (44763719)|VARIABLE_ASSIGNMENT|[14]|this.Name|"bote cuatro"|0x166f1a88
15:53:10.0 (44773007)|HEAP_ALLOCATE|[14]|Bytes:28
15:53:10.0 (44787176)|VARIABLE_ASSIGNMENT|[14]|this.Precio__c|700|0x166f1a88
15:53:10.0 (44802680)|VARIABLE_ASSIGNMENT|[14]|this.Tipo__c|"Velero"|0x166f1a88
15:53:10.0 (44809392)|HEAP_ALLOCATE|[14]|Bytes:5
15:53:10.0 (44822679)|VARIABLE_ASSIGNMENT|[14]|this.Ubicacion__c|"India"|0x166f1a88
15:53:10.0 (44842254)|VARIABLE_ASSIGNMENT|[14]|this.Contacto__c|"0036g000003JGBQAA4"|0x166f1a88
15:53:10.0 (44865940)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
15:53:10.0 (44873492)|STATEMENT_EXECUTE|[15]
15:53:10.0 (44926564)|DML_BEGIN|[15]|Op:Insert|Type:Bote__c|Rows:4
15:53:10.0 (44964450)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:20
15:53:10.0 (72326797)|DML_END|[15]
15:53:10.0 (72390487)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:20
15:53:10.0 (72413512)|STATEMENT_EXECUTE|[17]
15:53:10.0 (72493310)|HEAP_ALLOCATE|[17]|Bytes:74
15:53:10.0 (72517888)|SYSTEM_METHOD_ENTRY|[1]|Test.Test()
15:53:10.0 (72522740)|STATEMENT_EXECUTE|[1]
15:53:10.0 (72531956)|SYSTEM_METHOD_EXIT|[1]|Test
15:53:10.0 (72546906)|METHOD_ENTRY|[17]||System.Test.startTest()
15:53:10.0 (74088627)|METHOD_EXIT|[17]||System.Test.startTest()
15:53:10.0 (74103320)|STATEMENT_EXECUTE|[18]
15:53:10.0 (74187676)|VARIABLE_ASSIGNMENT|[18]|this.Ubicacion_Bote__c|"India"|0x50112970
15:53:10.0 (74195998)|STATEMENT_EXECUTE|[19]
15:53:10.0 (74227386)|HEAP_ALLOCATE|[19]|Bytes:8
15:53:10.0 (74238598)|DML_BEGIN|[19]|Op:Update|Type:Contact|Rows:1
15:53:10.0 (74269126)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:8
15:53:10.0 (93156030)|DML_END|[19]
15:53:10.0 (93180881)|STATEMENT_EXECUTE|[20]
15:53:10.0 (93203961)|METHOD_ENTRY|[20]||System.Test.stopTest()
15:53:10.0 (94230735)|METHOD_EXIT|[20]||System.Test.stopTest()
15:53:10.0 (94242441)|STATEMENT_EXECUTE|[22]
15:53:10.0 (94290037)|HEAP_ALLOCATE|[22]|Bytes:85
15:53:10.0 (94322468)|HEAP_ALLOCATE|[22]|Bytes:4
15:53:10.0 (94333580)|HEAP_ALLOCATE|[22]|Bytes:7
15:53:10.0 (94622558)|SOQL_EXECUTE_BEGIN|[22]|Aggregations:0|SELECT COUNT() FROM Bote__c WHERE (Contacto__c = :tmpVar1 AND Ubicacion__c = 'India')
15:53:10.0 (106753105)|SOQL_EXECUTE_END|[22]|Rows:1
15:53:10.0 (106780527)|HEAP_ALLOCATE|[22]|Bytes:4
15:53:10.0 (106904897)|EXCEPTION_THROWN|[22]|System.AssertException: Assertion Failed: Expected: 4, Actual: 1
15:53:10.0 (107106747)|HEAP_ALLOCATE|[22]|Bytes:44
15:53:10.0 (107246882)|FATAL_ERROR|System.AssertException: Assertion Failed: Expected: 4, Actual: 1

Class.BotesTestUnitarios.TestActualizar: line 22, column 1
15:53:10.0 (107262154)|FATAL_ERROR|System.AssertException: Assertion Failed: Expected: 4, Actual: 1

Class.BotesTestUnitarios.TestActualizar: line 22, column 1
15:53:10.107 (107272319)|CUMULATIVE_LIMIT_USAGE
15:53:10.107 (107272319)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 1 out of 100
  Number of query rows: 1 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 2 out of 150
  Number of DML rows: 5 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

15:53:10.107 (107272319)|TESTING_LIMITS
15:53:10.107 (107272319)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 0 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 1 out of 150
  Number of DML rows: 1 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

15:53:10.107 (107272319)|CUMULATIVE_LIMIT_USAGE_END

15:53:10.0 (107316532)|CODE_UNIT_FINISHED|BotesTestUnitarios.TestActualizar()
15:53:10.0 (108614235)|EXECUTION_FINISHED

 
Nayana KNayana K
Looking into the debug log,  I can see that after update trigger logic is not fired. 

1. Is the trigger active? 
2. Can you please post extact trigger code which you have deployed. Is it same what I have given? 
DiegoT.DiegoT.
trigger  

trigger active
DiegoT.DiegoT.
p.data : i have varaibles in spanish 
Nayana KNayana K
I want to check the full trigger. 

mainly first line of the trigger is having after update in the context right? 
trigger UpdateLocation on Contact (after update) { 
DiegoT.DiegoT.
trigger3
Nayana KNayana K
Please notice the first line :( you have written before insert. 
It should be after update (As per the code you have shared in the question).

please change it
This was selected as the best answer
DiegoT.DiegoT.
Oh thank you so much Nayana it is working, do you put after update in your first comment. here the correct trigger for this example :D
trigger UpdateLocation on Contact (after insert) {
   if(trigger.isAfter && Trigger.isUpdate) {
         List<Bote__c> lstBoatToUpdate = new List<Bote__c>();
         Map<Id, String> mapConIdToLocation = new Map<Id, String>();
         String temp;
         
         for(Contact objContact : Trigger.New)
         {
            /* if location is changed, then only update the relevant boats. Unnecessarily don't update the boats*/
            if(Trigger.oldMap.get(objContact.Id).Ubicacion_bote__c != objContact.Ubicacion_bote__c) {
                mapConIdToLocation.put(objContact.Id, objContact.Ubicacion_bote__c);
            }
         }
         // don't do unncessarily query. 
         if(!mapConIdToLocation.isEmpty()) {
            
            for (Bote__c objBoat : [SELECT Ubicacion__c, Contacto__c FROM Bote__c WHERE Contacto__c IN: mapConIdToLocation.keySet()]) {
                 temp = mapConIdToLocation.get(objBoat.Contacto__c);
                 /* If location of boat is not matching with contact.location_boat__c, then only update the boat*/
                 if(objBoat.Ubicacion__c != temp) {
                    objBoat.Ubicacion__c = temp;
                    lstBoatToUpdate.add(objBoat);
                 }
             }
             // update only if we have records in boat list
             if(!lstBoatToUpdate.isEmpty()) {
                update lstBoatToUpdate;
             }
         }
     }
    
}
Nayana KNayana K
I recommend you to test the functionality of the trigger with all the Usecases first(latest code what I have modified) .  If you feel,  trigger is working fine.  Then execute the test class. 
DiegoT.DiegoT.

Any extra bibliography that you recommend (also of trailmix or apex documentation)
Nayana KNayana K
Many people recommend http://www.sfdc99.com/.
Also,  go through standard apex documentation. Yes, you can start with trailhead modules. I don't have link. 

I have been given training in my company + coding tasks . Hence,  I didn't really refer online stuff.
Il share list of trigger tasks which you can try in free time. 
DiegoT.DiegoT.
User-added image 
I have last question Nayanam why result of the query is empty? 
what do you recommend to see the results of query of a test?
Nayana KNayana K
Hi Diego,
Does it really has records? Create a object tab for Bote and see if record is present in listview.