Your trigger is wrong. It introduces an infinite loop, so your code will crash. Instead, use before insert/before update. Also, there's no need to copy the list. Your entire trigger can be written as:
trigger ProjectHoursTrigger on Project_Hours__c (before insert, before update) {
UpdatePayrollPeriod.updatePayrollPeriod(Trigger.new);
}
As for the actual error, you should be checking if the fields are null first:
public class UpdatePayrollPeriod {
public static void updatePayrollPeriod(List<Project_Hours__c> projectHoursList) {
for (Project_Hours__c ph : projectHoursList){
if(ph.Year__c != null && ph.Date__c != null) {
First_Pay_Period__c p = First_Pay_Period__c.getValues(string.valueOf(ph.Year__c));
if(p != null && p.End_of_First_Pay_Period__c != null) {
ph.Payroll_Period2__c = ((p.End_of_First_Pay_Period__c.daysBetween(ph.Date__c))/14)+1;
}
}
}
}
}
For code coverage purposes, make sure your unit test is initializing the Date__c field, and the Year__c field, assuming it's not a formula.
Finally, it looks like you're using SeeAllData=false (the default test isolation mode), so you should also insert a new First_Pay_Period__c record before inserting the Project_Hours__c record.
Edit: The "p" variable in your class was not, not the field itself. This is because, as the last paragraph notes, you did not insert a First_Pay_Period__c record.
Edit 2: After a DML statement, you need to query the records back from the database to see what the final saved values are:
testHours = [SELECT PayRoll_Period2__c FROM Project_Hours__c WHERE Id = :testHours.Id];
System.assertEquals(testHours.Payroll_Period2__c, 2);
Best Answer
You can't insert your Account outside your test method and access it directly.
You have two solutions: