What keeps bitcoins mined in testnet from being used in mainnet? I've been reading up on how transactions actually work, but I'm not sure I understand how testnet bitcoins are kept from being spent on mainnet. My best understanding is that coins are actually references to transactions on the chain, so referencing a testnet transaction on the mainnet chain would not work. Is this correct?
Yes, that's correct. A mainnet node would just see those transactions as invalid because the outputs they attempt to spend are not in its database. It would have no idea that there is some other network on which they are valid.