Unit-testing – Is testing behavior of many classes in one test still unit testing

integration-testsunit testing

Our project's policy is to write unit tests for single classes only. All dependencies are mocked. Recently we've noticed that this approach makes us vulnerable in such cases:

Originally class A looks like this:

class A(val b: B) {

   fun doSomething() {
       b.doSomethingElse()
       b.doSomethingElse2()
   }
}

And such class A is covered with unit tests. Then due to new requirements class B goes through refactoring and gets hidden behind an interface so that it is technically possible that class A gets a different behavior based on a scenario.

The problem is that now, when we want to follow our project's guidelines, we also should refactor A's unit tests. Previously there was a test for proper communication with an object of B (mocked of course). Now when reference to B is gone then the test is refactored to verify communication with this new interface.

I find this scenario as an information loss happening during this test refactoring. We are no longer verifying any communication between A->B due to unit test purity when in real system such communication exists.

Having that in mind, should we change our way of thinking about unit tests and create test cases where there is more than one real object? Would it still be unit testing or is it already integration testing?

Best Answer

The outer limit of a unit is IO. If you're talking to peripheral's you ain't unit testing no more.

But within that you can carve things up as thickly or as finely as you see fit.

A test can exercise methods from three objects and still be a unit test. Some may claim that's integration but three lines of code are just as "integrated" so that's not really a thing. Method and object boundaries don't present any significant barrier to testing. Peripheral's do.

A test is not a unit test if:

  • It talks to the database
  • It communicates across the network
  • It touches the file system
  • It can't run at the same time as any of your other unit tests
  • You have to do special things to your environment (such as editing config files) to run it.

Michael Feathers - Working Effectively with Legacy Code

Now sure, you can argue that some people consider every test that can be automated by a unit testing frame work a unit test.

If we just want to argue semantics then fine, I'll define a unit as a beautiful flower in a field that smells bad.

Regardless of the terms, what is needed is segregation of slow troublesome tests from tests so blindingly fast and stable that you can run them while you type.

Call what they act on what you will. Just don't mix them up.

Related Topic