1

I believe the consensus on unit tests is that each individual test should interact with the smallest surface possible (?).

I have a function I want to test, but it depends on some setup performed by other function.

Should I call these other function in my test, or manually setup the state as the tested function expects?

I.e.

test function_remove_item:
  // setup the test
  state = clone(initial_state)
  item = new Item('an-id')
  // !!! Call another function to setup the test state
  add_item(state, item) // blindly assume that add_item is functioning correctly (test it in a different test)

  // perform the test
  remove_item(state, item)
  expect(state.items).notToInclude(item)
  expect(state.ordered).NotToInclude('my-id')
  expect(state.some_other_complex_state).toPass()

vs

test function_remove_item
  state = clone(initial_state)
  // manually perform all the state setup without hitting add_item
  state.items.push(new Item('my-id'))
  state.ordered.push('my-id')
  state.some_other_complex_state = true
  // imagine remove_item may call other function that rely on
  // some other state
  state.nested_flag_for_function_check_item_status = true

  // perform the test
  remove_item(state, item)
  expect(state.items).notToInclude(item)
  expect(state.ordered).NotToInclude('my-id')
  expect(state.some_other_complex_state).toPass()

It seems that calling add_item in my test means the test is probably more maintainable since it's harder to misconfigure the state (and the requirements of that state may grow quite large) but also makes the test feel somehow "un-pure"?

2

I believe the consensus on unit tests is that each individual test should interact with the smallest surface possible

That is "consensus", but I disagree. Test being fast, isolated and meaningful is much more important than how much code it tests. And it doesn't matter if they are "unit" or "integration" tests if they provide value and aid in development and design.

In your case, it is perfectly reasonable to call multiple methods if that is how it is expected the object will be used.

2

I believe the consensus on unit tests is that each individual test should interact with the smallest surface possible

Personally, I find that testing at the module grain works best - trying to constrain the behavior of smaller components is counter productive. Remember, what we really care about is behavior, not implementation.

Should I call these other function in my test, or manually setup the state as the tested function expects?

One interesting heuristic for test design is that tests should be executable examples.

When you are writing software, including executable examples, your focus should be on clarity of intent. If the code could be any more obvious, you’re probably not done yet.

The examples tell a story about what the code does. They are the documentation narrative that will guide future programmers (including yourself when you come back to change this code in three months time and you’ve forgotten what it does)

Note that there is a very important design pressure here -- if establishing the context for your example requires pulling in a lot of superfluous details, that may be trying to call your attention to accidental complexity (coupling).

0

A unit test should be testing one particular aspect of a method or class. If you are required to perform certain setup actions prior to the test which might fail, then I think all this amounts to is simply having a dedicated unit test for the setup itself so that should that test fail due to the setup, the setup test itself would fail as well.

Simply not testing the setup means not being able to determine the cause of the failure should it happen since it could fail in the setup.

That said, it is certainly not the most crucial thing. The crucial point is that there would be a failure should something not go as expected, even if that failure happens in the setup. Ideally you want to minimize what you're testing, but not at the expense of readability. So in conclusion, I would rather have a simpler test which may fail for reasons other than what I'm testing, so long as I'm testing also this in some other unit test.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.