The question seems to be asking specifically about System Testing, so that's what I'm referring to throughout this answer.
I think there's an important distinction to be made between being a bad person to choose to perform testing, and actually being bad at testing.
Why programmers are bad at testing:
- If you've written the code, you (should) have already thought of as many ways as possible that things could go wrong, and have dealt with them.
- If finding a particularly niggly bug means that you have to then go and fix it, in a codebase you might be sick of, then that isn't going to help your motivation.
Why programmers are good at testing:
- Programmers tend to be logical thinkers, and good at working in a systematic way.
- Experienced programmers will be very good at quickly identifying edge cases and so coming up with useful tests. (If there's a formalised testing process, then most all of these cases should already have been identified and tested prior to systems testing.)
- Programmers are pretty good at making sure that all the useful information goes into a bug report.
Why programmers are bad testers:
- Programmers are more expensive than testers (in the vast majority of cases).
- The mindset is fundamentally different: "Build a (working) product" vs "This thing isn't going out the door with any (unknown) bugs in it."
- Testers will typically be more efficient - i.e. perform more tests in the same amount of time.
- Programmers specialise in programming. QA professionals specialise in testing.
It's comparing oranges and apples.
Integration tests, acceptance tests, unit tests, behaviour tests - they are all tests and they will all help you improve your code but they are also quite different.
I'm going to go over each of the different tests in my opinion and hopefully explain why you need a blend of all of them:
Integration tests:
Simply, test that different component parts of your system integrate correctly - for example - maybe you simulate a web service request and check that the result comes back. I would generally use real (ish) static data and mocked dependencies to ensure that it can be consistently verified.
Acceptance tests:
An acceptance test should directly correlate to a business use case. It can be huge ("trades are submitted correctly") or tiny ("filter successfully filters a list") - it doesn't matter; what matters is that it should be explicitly tied to a specific user requirement. I like to focus on these for test-driven development because it means we have a good reference manual of tests to user stories for dev and qa to verify.
Unit tests:
For small discrete units of functionality that may or may not make up an individual user story by itself - for example, a user story which says that we retrieve all customers when we access a specific web page can be an acceptance test (simulate hitting the web page and checking the response) but may also contain several unit tests (verify that security permissions are checked, verify that the database connection queries correctly, verify that any code limiting the number of results is executed correctly) - these are all "unit tests" that aren't a complete acceptance test.
Behaviour tests:
Define what the flow should be of an application in the case of a specific input. For example, "when connection cannot be established, verify that the system retries the connection." Again, this is unlikely to be a full acceptance test but it still allows you to verify something useful.
These are all in my opinion through much experience of writing tests; I don't like to focus on the textbook approaches - rather, focus on what gives your tests value.
Best Answer
This is more guidance than hard-and-fast rule.
By using "new" in your production code, you are coupling your class with its collaborators. If someone wants to use some other collaborator, for example some kind of mock collaborator for unit testing, they can't – because the collaborator is created in your business logic.
Of course, someone needs to create these new objects, but this is often best left to one of two places: a dependency injection framework like Spring, or else in whichever class is instantiating your business logic class, injected through the constructor.
Of course, you can take this too far. If you want to return a new ArrayList, then you are probably OK – especially if this is going to be an immutable List.
The main question you should be asking yourself is "is the main responsibility of this bit of code to create objects of this type, or is this just an implementation detail I could reasonably move somewhere else?"