Documentation>Testing & Debugging Scripts
Essential Skills

Testing & Debugging Scripts

Comprehensive guide to testing your Google Ads scripts safely before deploying to live campaigns and debugging common issues.

22 minutes
Preview Mode: Your Safety Net

🛡️ Always Test in Preview First

Preview mode shows exactly what your script will do without making any actual changes. This is your most important testing tool.

How to use Preview:
1. Click "Preview" instead of "Run"
2. Review all logged actions carefully
3. Verify the changes make sense
4. Only then click "Run" to execute
Debug Logging
  • Detailed execution logs
  • Variable state tracking
  • Performance monitoring
  • Error capture
Error Handling
  • Try-catch blocks
  • Graceful degradation
  • Error notifications
  • Recovery procedures
Validation
  • Input validation
  • Boundary testing
  • Edge case handling
  • Result verification
Effective Debug Logging

❌ Poor Logging

// Bad: Minimal information
function main() {
  Logger.log("Starting");
  
  const campaigns = AdsApp.campaigns().get();
  while (campaigns.hasNext()) {
    const campaign = campaigns.next();
    // No logging of what's happening
    campaign.setBudget(1000);
  }
  
  Logger.log("Done");
}

✅ Comprehensive Logging

// Good: Detailed progress tracking
function main() {
  Logger.log("=== Budget Optimization Started ===");
  Logger.log(`Target ROAS: ${TARGET_ROAS}`);
  
  const campaigns = AdsApp.campaigns()
    .withCondition('Status = ENABLED')
    .get();
    
  Logger.log(`Found ${campaigns.totalNumEntities()} campaigns to process`);
  
  let processed = 0;
  while (campaigns.hasNext()) {
    const campaign = campaigns.next();
    const oldBudget = campaign.getBudget();
    
    Logger.log(`Processing: ${campaign.getName()}`);
    Logger.log(`  Current budget: €${oldBudget}`);
    
    const newBudget = calculateOptimalBudget(campaign);
    campaign.setBudget(newBudget);
    processed++;
    
    Logger.log(`  New budget: €${newBudget} (change: ${((newBudget/oldBudget-1)*100).toFixed(1)}%)`);
  }
  
  Logger.log(`=== Completed: ${processed} campaigns optimized ===`);
}

🔍 Logging Best Practices

  • ✅ Log script start/end with timestamps
  • ✅ Show progress for long operations
  • ✅ Log before and after values
  • ✅ Include campaign/keyword names
  • ✅ Use consistent formatting
  • ✅ Log decision logic reasoning
  • ✅ Include error context
  • ✅ Log summary statistics
Robust Error Handling
// Comprehensive error handling example
function main() {
  try {
    Logger.log("Starting budget optimization...");
    
    const config = validateConfiguration();
    const campaigns = getCampaignsToOptimize();
    
    if (campaigns.totalNumEntities() === 0) {
      Logger.log("WARNING: No campaigns found matching criteria");
      return;
    }
    
    let processed = 0;
    let errors = 0;
    
    while (campaigns.hasNext()) {
      try {
        const campaign = campaigns.next();
        
        // Validate campaign state
        if (!campaign.isEnabled()) {
          Logger.log(`SKIP: ${campaign.getName()} is not enabled`);
          continue;
        }
        
        const result = optimizeCampaignBudget(campaign, config);
        
        if (result.success) {
          processed++;
          Logger.log(`SUCCESS: ${campaign.getName()} - ${result.message}`);
        } else {
          errors++;
          Logger.log(`ERROR: ${campaign.getName()} - ${result.error}`);
        }
        
      } catch (campaignError) {
        errors++;
        Logger.log(`CAMPAIGN ERROR: ${campaignError.message}`);
        // Continue processing other campaigns
      }
    }
    
    // Final summary
    Logger.log(`SUMMARY: ${processed} successful, ${errors} errors`);
    
    if (errors > 0) {
      Logger.log("WARNING: Some campaigns could not be optimized. Check logs above.");
    }
    
  } catch (mainError) {
    Logger.log(`FATAL ERROR: ${mainError.message}`);
    Logger.log("Stack trace: " + mainError.stack);
    
    // Send alert email for critical failures
    sendErrorNotification(mainError);
  }
}

function optimizeCampaignBudget(campaign, config) {
  try {
    // Validation
    const currentBudget = campaign.getBudget();
    if (currentBudget <= 0) {
      return { success: false, error: "Invalid current budget" };
    }
    
    // Calculate new budget
    const stats = campaign.getStatsFor('LAST_30_DAYS');
    const newBudget = calculateOptimalBudget(stats, config);
    
    // Safety checks
    if (newBudget > config.maxBudget) {
      return { success: false, error: `Exceeds max budget: €${config.maxBudget}` };
    }
    
    campaign.setBudget(newBudget);
    return { 
      success: true, 
      message: `Budget updated: €${currentBudget} → €${newBudget}`
    };
    
  } catch (error) {
    return { success: false, error: error.message };
  }
}

🚨 Common Error Types

  • API limits: Too many operations
  • Permissions: Insufficient access
  • Data issues: Missing or invalid data
  • Network: Connection problems
  • Logic errors: Incorrect calculations

✅ Error Handling Strategy

  • • Wrap risky operations in try-catch
  • • Validate inputs before processing
  • • Provide fallback behaviors
  • • Log detailed error information
  • • Continue processing when possible
Testing Strategies

🧪 Testing Phases

1. Preview Testing
  • • Use preview mode exclusively
  • • Test with small data sets
  • • Verify logic flow
  • • Check calculations
2. Limited Live Testing
  • • Test on 1-2 campaigns only
  • • Use conservative changes
  • • Monitor results closely
  • • Have rollback plan ready
3. Full Deployment
  • • Gradual rollout
  • • Continuous monitoring
  • • Performance tracking
  • • Regular optimizations

🎯 Test Scenarios

  • • Empty data sets (no campaigns found)
  • • Campaigns with zero performance
  • • Very high/low budgets
  • • Paused or deleted campaigns
  • • Network/API failures

📋 Testing Checklist

  • ☐ Preview mode test passed
  • ☐ Edge cases handled
  • ☐ Error scenarios tested
  • ☐ Logging is comprehensive
  • ☐ Performance within limits
  • ☐ Rollback plan prepared
🐛 Common Issues & Solutions

Script timeout (30 minutes exceeded)

→ Use batching, add progress tracking, limit data scope with .withCondition()

Operation limit exceeded (250,000 ops)

→ Track operation count, process fewer entities, use efficient filtering

Insufficient permissions

→ Check account access level, verify script authorization, test with admin account

Data not found errors

→ Add null checks, validate entity exists, handle empty result sets gracefully