> Should savePeople() be unit tested Yes, it should. But try to write your test conditions in a way that is independent from the implementation. For example, turning your usage example into a unit test: function testSavePeople() { myDataStore = new Store('some connection string', 'password'); myPeople = ['Joe', 'Maggie', 'John']; savePeople(myDataStore, myPeople); assert(myDataStore.containsPerson('Joe')); assert(myDataStore.containsPerson('Maggie')); assert(myDataStore.containsPerson('John')); } This test does multiple things: - it verifies the contract of the function `savePeople()` - it does not care about the implementation of `savePeople()` - it documents the example usage of `savePeople()` Take note that you can still mock/stub/fake the data store. In this case I wouldn't check for explicit function calls, but for the result of the operation. This way my test is prepared for future changes/refactors. For example, your data store implementation might provide a `saveBulkPerson()` method in the future - now a change to the implementation of `savePeople()` to use `saveBulkPerson()` would not break the unit test as long as `saveBulkPerson()` works as expected. And if `saveBulkPerson()` somehow does not work as expected, your unit test *will* catch that. > or would such tests amount to testing the built-in forEach language construct? As said, try to test for expected results and the function interface, not for the implementation (unless you are doing integration tests - then catching specific function calls might be of use). *If there are multiple ways to implement a function, all of them should work with your unit test.* Regarding your update of the question: Test for state changes! E.g. some of the dough will be used. According to your implementation, assert that the amount of used `dough` fits into `pan` or assert that the `dough` is used up. Assert that the `pan` contains cookies after the function call. Assert that the `oven` is empty/in the same state as before. For additional tests, verify edge cases: What happens if the `oven` is not empty before the call? What happens if there isn't enough `dough`? If the `pan` is already full? (Additional thought exercise: should your unit test break if someone adds a line `oven.preheat()`?) You should be able to deduce all the required data for these tests from the dough, pan and oven objects themselves. No need to capture the function calls. Treat the function as if its implementation would not be available to you! In fact, most TDD users write their tests before they write the function so they are not dependent on the actual implementation.