The error itself is pretty straightforward. If your record has an Id already, then it cannot be inserted. Inserting a record is what gives it an Id in the first place. So trying to insert a record that already has an Id is not a valid operation.
Test.loadData() itself doesn't cause records to be inserted, so that means the static resource you're passing into
Test.loadData() contains Id values. Modify your static resource file appropriately (so you don't have an Id column or Id values), re-upload it, and things should work better.
Your code (and test code) has some issues you should address.
Bad variable names are going to haunt you later
what is "lst"? what is "c"?
It's easy enough to see what they are now, when the amount of code you have is so small, but being more descriptive with your variable names will help you (and whoever inherits your code after you).
invoiceLineItem are much better variable names (in your test class).
It's also helpful to have a convention for naming things. Something like:
- Make class names TitleCase
- Method names start with a lowercase letter, and then useCamelCase
- Indicate a collection by using the plural (e.g.
It doesn't matter what you settle on, just pick something, stick to it, and be consistent
DML inside of a loop
DML and Queries are things that should practically never appear inside of a loop. Salesforce only gives us so many of each for any given transaction, and putting queries or DML inside of a loop is a ticket on the express train to a governor limit exception.
The key to working on the Salesforce platform is to use collections (lists, sets, and maps) as much as you can.
You should remove the loop in your test class and just simply insert the list of records
For the SUT (system under test, i.e. the trigger you're trying to test here), you also have DML inside of a loop. Generally, the appropriate way to do this is to add your records to a list (done inside of your loop). Then, once you're done looping, you perform dml on the entire list at once (as opposed to one at a time).
It's not very clear what your trigger is trying to do (are you trying to sum all invoice line items on a given invoice? are you trying to sum all line items and put them under a single invoice?), so it's hard to tell you what you should do specifically here.
You'll either want to create a list, or just keep adding to
sum and set/update the invoice once. In both cases, you do not want to have DML inside of a loop.
Also, in both cases, this trigger is unlikely to do what you want it to.
If any of your
Amount_Item_* fields are null, you'll run into an error
Math operators don't handle null values. You could do manual null checking (
Amount_Item_One__c == null ? 0 : Amount_Item_One__c will return 0 if null, and the given amount if not null), but honestly, there's a better way to do this...
Sometimes, code isn't the best approach
You can do the work of summing your three Amount fields by creating a formula field on your
Invoice_Line_Item__c SObject. Even better, you can tell formula fields how to handle null values ("treat blanks as zeroes" is the setting you'd want).
If you only want to sum up those values on each line item (and not roll the total up to the parent
Invoice__c record), then your work would be done (no need for a trigger and no need for a test).
If you're trying to roll up data from child records to a parent record, you should consider making
Invoice_Line_Item__c part of a master-detail relationship (Invoice is the parent/master, Invoice Line Item is the child/detail). That would allow you to create rollup summary field(s) on
Invoice__c to do this work automatically (and without the need for a test).
If you combine both of these (formula field on Invoice Line Item, rollup summary on Invoice), then your work would be done.
If that's not a possibility, then you should look into Andrew Fawcett's Declarative Lookup Rollup Summary tool before trying to write your own trigger to do field roll-ups.