Skip to content

Instantly share code, notes, and snippets.

@niavesper
Last active April 14, 2021 18:14
Show Gist options
  • Save niavesper/59e0cdbfe02fb8609b672bfd3a3c3bab to your computer and use it in GitHub Desktop.
Save niavesper/59e0cdbfe02fb8609b672bfd3a3c3bab to your computer and use it in GitHub Desktop.
RecipeHandler2.cls: First method throws an error when one of the key fields is blank, which is the requirement of Week 6 homework. RecipeHandler2_Test contains testing for exception throwing: positive (lines 25-93) and negative (lines 178-223). RecipeTrigger2.trigger calls on the exception-throwing method
public with sharing class RecipeHandler2 {
// Week 6 homework: Exception Testing
public static void throwException(List<Recipe__c> exceptionRecipes){
for (Recipe__c r : exceptionRecipes){
if (r.Name == null || r.Active_Time__c == null || r.Description__c == null || r.Active_Time_Units__c == null || r.Servings__c == null){
r.addError('One of the key ingredients is null!');
}
}
}
public static void rateComplexity(List<Recipe__c> ratedRecipes) {
List <Recipe__c> updatedComplexityRecipes = new List <Recipe__c>();
for (Recipe__c r : ratedRecipes){
switch on HelperFunctions.rateRecipeComplexity (r){
when 1 {
r.Complexity__c = 'Simple';
}
when 2 {
r.Complexity__c = 'Moderate';
}
when 3 {
r.Complexity__c = 'Difficult';
}
}
updatedComplexityRecipes.add(r);
}
}
public static void createTasks(List<Recipe__c> taskRecipes) {
List <Task> newTasks = new List<Task>();
for (Cookbook__c c : [SELECT Id, OwnerId FROM Cookbook__c
WHERE ID IN (SELECT Cookbook__c FROM Recipe_Usage__c
WHERE Recipe__r.Draft__c = false AND Recipe__r.Id IN : taskRecipes)]){
Task t = new Task();
t.WhatId = c.Id;
t.OwnerId = c.OwnerId;
t.ActivityDate = Date.Today().addDays(7);
t.Subject = 'Review recipe';
t.Status = 'Not Started';
newTasks.add(t);
}
insert newTasks;
}
}
@isTest
private class RecipeHandler2_Test {
@testSetup
static void dataCreation(){
// create data for testing recipe Complexity
List <Recipe__c> complexityRecipes = new List<Recipe__c>();
for(integer i = 0; i < 3; i++){
Recipe__c r = new Recipe__c(
Name = 'Complexity Recipe ' + i,
Active_Time__c = i * 30,
Active_Time_Units__c = 'Minutes',
Servings__c = i + 6,
Description__c = 'Test Desc');
HelperFunctions.rateRecipeComplexity (r);
complexityRecipes.add(r);
}
// insert data
insert complexityRecipes;
}
/*** positive testing for before insert of the "throwException" method***/
@isTest
static void throwException_InsertPositive(){
// create test data
List <Recipe__c> draftRecipes = new List<Recipe__c>();
for(integer i=0; i < 4; i++){
Recipe__c r = new Recipe__c(
Name = 'Draft Recipe ' + i,
Active_Time__c = i,
Description__c = 'Test Description ' + i,
Active_Time_Units__c = 'Minutes',
Servings__c = i);
draftRecipes.add(r);
}
// assign blank values to key fields: one blank key field per record
draftRecipes[0].Active_Time__c = null;
draftRecipes[1].Description__c = null;
draftRecipes[2].Active_Time_Units__c = null;
draftRecipes[3].Servings__c = null;
// Insert data and catch exception
Test.startTest();
try{
insert draftRecipes;
} catch (Exception e) {
// assert: the type of error we get should be 'System.DmlException'
System.assertEquals(e.getTypeName(), 'System.DmlException');
}
Test.stopTest();
}
/*** positive testing for before update of the "throwException" method***/
@isTest
static void throwException_UpdatePositive(){
List <Recipe__c> draftRecipes = new List<Recipe__c>();
for(integer i=0; i < 4; i++){
Recipe__c r = new Recipe__c(
Name = 'Draft Recipe ' + i,
Active_Time__c = i,
Description__c = 'Test Description ' + i,
Active_Time_Units__c = 'Minutes',
Servings__c = i);
draftRecipes.add(r);
}
insert draftRecipes;
// assign blank values to key fields: one blank key field per record
draftRecipes[0].Active_Time__c = null;
draftRecipes[1].Description__c = null;
draftRecipes[2].Active_Time_Units__c = null;
draftRecipes[3].Servings__c = null;
// Update data and catch exception
Test.startTest();
try{
update draftRecipes;
} catch (Exception e) {
// assert: the type of error we get should be 'System.DmlException'
System.assertEquals(e.getTypeName(), 'System.DmlException');
}
Test.stopTest();
}
/*** positive testing for before insert of the "rateComplexity" method***/
@isTest
static void addComplexityRating_InsertPositive(){
// query ratings of inserted recipes and assign them to strings for assertion
String ratingZero = [SELECT Id, Complexity__c FROM Recipe__c WHERE Name = 'Complexity Recipe 0'].Complexity__c;
String ratingOne = [SELECT Id, Complexity__c FROM Recipe__c WHERE Name = 'Complexity Recipe 1'].Complexity__c;
String ratingTwo = [SELECT Id, Complexity__c FROM Recipe__c WHERE Name = 'Complexity Recipe 2'].Complexity__c;
// assert: compare expected values of *inserted* records to actual
System.assertEquals('Simple', ratingZero, 'Rating Zero should be "Simple"');
System.assertEquals('Moderate', ratingOne, 'Rating One should be "Moderate"');
System.assertEquals('Difficult', ratingTwo, 'Rating Tw0 should be "Difficult"');
}
/*** positive testing for before update of the "rateComplexity" method ***/
@isTest
static void addComplexityRating_UpdatePositive(){
// Create a list of inserted recipes
List <Recipe__c> recipesUpdatedWithComplexity = [SELECT Id FROM Recipe__c WHERE Name LIKE 'Complexity%' AND Complexity__c != null];
// update data
Test.startTest();
update recipesUpdatedWithComplexity;
Test.stopTest();
// query ratings of updated recipes and assign them to strings for assertion
String ratingZero = [SELECT Id, Complexity__c FROM Recipe__c WHERE Name = 'Complexity Recipe 0'].Complexity__c;
String ratingOne = [SELECT Id, Complexity__c FROM Recipe__c WHERE Name = 'Complexity Recipe 1'].Complexity__c;
String ratingTwo = [SELECT Id, Complexity__c FROM Recipe__c WHERE Name = 'Complexity Recipe 2'].Complexity__c;
// assert: compare expected values of *updated* records to actual
System.assertEquals('Simple', ratingZero, 'Rating Zero should be "Simple"');
System.assertEquals('Moderate', ratingOne, 'Rating One should be "Moderate"');
System.assertEquals('Difficult', ratingTwo, 'Rating Two should be "Difficult"');
}
/*** positive testing for after update of the "createTasks" method***/
@isTest
static void createTasksOnCookbook_UpdatePositive(){
// create and insert test data (new non-draft Recipe and Cookbook).
List<sObject> recordsToInsert = new List<sObject>();
recordsToInsert.add (new Recipe__c (Name = 'Task Recipe',
Active_Time__c = 2,
Active_Time_Units__c = 'Hours',
Description__c = 'Test Description',
Servings__c = 8
));
recordsToInsert.add (new Cookbook__c (Name = 'Test Cookbook'));
insert recordsToInsert;
// query newly created records to use their IDs on the record of the junction object
Recipe__c newRecipe = [SELECT Id FROM Recipe__c WHERE Name = 'Task Recipe'];
Cookbook__c newCookbook = [SELECT Id, OwnerId FROM Cookbook__c];
// create test data (new Recipe Usage with lookups to earlier created Cookbook and Recipe)
Recipe_Usage__c newRecipeUsage = new Recipe_Usage__c (
Cookbook__c = newCookbook.Id,
Recipe__c = newRecipe.Id);
// insert test data
insert newRecipeUsage;
// update the recipe record
Test.startTest();
update newRecipe;
Test.stopTest();
// assign the task that should have been created to a list
List<Task> newTasks = [SELECT Id FROM Task
WHERE WhatId = : newCookbook.Id AND
OwnerId = : newCookbook.OwnerId AND
ActivityDate = NEXT_N_DAYS:7 AND
Subject = 'Review recipe' AND
Status = 'Not Started'
];
// assert: check to see if there is one task on the list that meets the conditions
System.assertEquals(1, newTasks.size(), 'One task should have been inserted');
}
/*** negative testing for before insert of the "throwException" method***/
@isTest
static void throwException_InsertNegative(){
List <Recipe__c> draftRecipes = new List<Recipe__c>();
for(integer i=0; i < 4; i++){
Recipe__c r = new Recipe__c(
Name = 'Draft Recipe ' + i,
Active_Time__c = i,
Description__c = 'Test Description ' + i,
Active_Time_Units__c = 'Minutes',
Servings__c = i);
draftRecipes.add(r);
}
Test.startTest();
insert draftRecipes;
Test.stopTest();
// assert: the type of error we get should be 'System.DmlException'
System.assertEquals(4, draftRecipes.size(), '4 recipes should have been inserted without error');
}
/*** negative testing for before update of the "throwException" method***/
@isTest
static void throwException_UpdateNegative(){
List <Recipe__c> draftRecipes = new List<Recipe__c>();
for(integer i=0; i < 4; i++){
Recipe__c r = new Recipe__c(
Name = 'Draft Recipe ' + i,
Active_Time__c = i,
Description__c = 'Test Description ' + i,
Active_Time_Units__c = 'Minutes',
Servings__c = i);
draftRecipes.add(r);
}
insert draftRecipes;
Test.startTest();
update draftRecipes;
Test.stopTest();
// assert: the type of error we get should be 'System.DmlException'
System.assertEquals(4, draftRecipes.size(), '4 recipes should have been inserted without error');
}
/*** Negative testing for after update of the "createTasks" method ***/
@isTest
static void createTasksOnCookbook_UpdateNegative(){
// create and insert test data (new draft Recipe and Cookbook).
List<sObject> recordsToInsert = new List<sObject>();
recordsToInsert.add (new Recipe__c (Name = 'Task Recipe',
Active_Time__c = 2,
Active_Time_Units__c = 'Hours',
Description__c = 'Test Description',
Servings__c = 8,
Draft__c = True
));
recordsToInsert.add (new Cookbook__c (Name = 'Test Cookbook'));
insert recordsToInsert;
// query newly created records to use their IDs on the record of the junction object
Recipe__c newRecipe = [SELECT Id FROM Recipe__c WHERE Name = 'Task Recipe'];
Cookbook__c newCookbook = [SELECT Id, OwnerId FROM Cookbook__c];
// create test data (new Recipe Usage with lookups to earlier created Cookbook and Recipe)
Recipe_Usage__c newRecipeUsage = new Recipe_Usage__c (
Cookbook__c = newCookbook.Id,
Recipe__c = newRecipe.Id);
// insert test data
insert newRecipeUsage;
// update the recipe record
Test.startTest();
update newRecipe;
Test.stopTest();
// assign the task that should have been created to a list
List<Task> newTasks = [SELECT Id FROM Task
WHERE WhatId = : newCookbook.Id AND
OwnerId = : newCookbook.OwnerId AND
ActivityDate = NEXT_N_DAYS:7 AND
Subject = 'Review recipe' AND
Status = 'Not Started'
];
// assert: check to see if there is one task on the list that meets the conditions
System.assertEquals(0, newTasks.size(), 'No tasks should have been inserted');
}
}
trigger RecipeTrigger2 on Recipe__c (before insert, after insert, before update, after update) {
if (trigger.isBefore && ( trigger.isInsert || trigger.isUpdate)){
RecipeHandler2.throwException(trigger.new);
RecipeHandler2.rateComplexity(trigger.new);
}
if(trigger.isAfter && trigger.isUpdate){
RecipeHandler2.createTasks(trigger.new);
}
}
@tugce
Copy link

tugce commented Apr 14, 2021

I think except the naming of positive/negative this looks good! 👏 Negative testing is when catching the unexpected situation, bad values etc, like error messages the way you added with addError. I know this sounds a bit counterproductive as that exception is expected but well 😄 Good job @niavesper

Negative tests verify that your code handles exceptions and errors well
Testing Unexpected Conditions
Bad Input Values
Boundary Conditions

Let me know if you have question on positive/negative testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment