What you describe may actually not be such a bad thing, but a pointer to deeper problems your tests discover
As the system changes we find ourselves spending more time fixing broken tests. We have unit, integration and functional tests.
If you could change your code, and your tests would not break, that would be suspicious to me. The difference between a legitimate change and a bug is only the fact that it is requested, an what is requested is (TDD assumed) defined by your tests.
data has been hard coded.
Hard coded data in tests is imho a good thing. Tests work as falsifications, not as proofs. If there is too much calculation, your tests may be tautologies. For example:
assert sum([1,2,3]) == 6
assert sum([1,2,3]) == 1 + 2 + 3
assert sum([1,2,3]) == reduce(operator.add, [1,2,3])
The higher the abstraction, the closer you get to the algorithm, and by that, closer to comparing the acutal implementation to itself.
very little reuse of code
The best reuse of code in tests is imho 'Checks', as in jUnits
assertThat, because they keep the tests simple.
Besides that, if the tests can be refactored to share code, the actual code tested likely can be too, thus reducing the tests to the ones testing the refactored base.