What are the pros and cons of automated Unit Tests

2019-01-21 05:58发布

Recently we have been adding automated tests to our existing java applications.

What we have

The majority of these tests are integration tests, which may cover a stack of calls like:-

  1. HTTP post into a servlet
  2. The servlet validates the request and calls the business layer
  3. The business layer does a bunch of stuff via hibernate etc and updates some database tables
  4. The servlet generates some XML, runs this through XSLT to produce response HTML.

We then verify that the servlet responded with the correct XML and that the correct rows exist in the database (our development Oracle instance). These rows are then deleted.

We also have a few smaller unit tests which check single method calls.

These tests are all run as part of our nightly (or adhoc) builds.

The Question

This seems good because we are checking the boundaries of our system: servlet request/response on one end and database on the other. If these work, then we are free to refactor or mess with anything inbetween and have some confidence that the servlet under test continues to work.

What problems are we likely to run into with this approach?

I can't see how adding a bunch more unit tests on individual classes would help. Wouldn't that make it harder to refactor as it's much more likely we will need to throw away and re-write tests?

12条回答
唯我独甜
2楼-- · 2019-01-21 06:28

Although the setup you described sounds good, unit testing also offers something important. Unit testing offers fine levels of granularity. With loose coupling and dependency injection, you can pretty much test every important case. You can be sure that the units are robust; you can scrutinise individual methods with scores of inputs or interesting things that don't necessarily occur during your integration tests.

E.g. if you want to deterministically see how a class will handle some sort of failure that would require a tricky setup (e.g. network exception when retrieving something from a server) you can easily write your own test double network connection class, inject it and tell it to throw an exception whenever you feel like it. You can then make sure that the class under test gracefully handles the exception and carries on in a valid state.

查看更多
冷血范
3楼-- · 2019-01-21 06:29

The thing that distinguishes Unit tests and Integration tests is the number of parts required for the test to run.

Unit tests (theoretically) require very (or no) other parts to run. Integration tests (theoretically) require lots (or all) other parts to run.

Integration tests test behaviour AND the infrastructure. Unit tests generally only test behaviour.

So, unit tests are good for testing some stuff, integration tests for other stuff.

So, why unit test?

For instance, it is very hard to test boundary conditions when integration testing. Example: a back end function expects a positive integer or 0, the front end does not allow entry of a negative integer, how do you ensure that the back end function behaves correctly when you pass a negative integer to it? Maybe the correct behaviour is to throw an exception. This is very hard to do with an integration test.

So, for this, you need a unit test (of the function).

Also, unit tests help eliminate problems found during integration tests. In your example above, there are a lot of points of failure for a single HTTP call:

the call from the HTTP client the servlet validation the call from the servlet to the business layer the business layer validation the database read (hibernate) the data transformation by the business layer the database write (hibernate) the data transformation -> XML the XSLT transformation -> HTML the transmission of the HTML -> client

For your integration tests to work, you need ALL of these processes to work correctly. For a Unit test of the servlet validation, you need only one. The servlet validation (which can be independent of everything else). A problem in one layer becomes easier to track down.

You need both Unit tests AND integration tests.

查看更多
Rolldiameter
4楼-- · 2019-01-21 06:34

I agree with Charlie about Integration-level tests corresponding more to user actions and the correctness of the system as a whole. I do think there is alot more value to Unit Tests than just localizing failures more tightly though. Unit tests provide two main values over integration tests:

1) Writing unit tests is as much an act of design as testing. If you practice Test Driven Development/Behavior Driven Development the act of writing the unit tests helps you design exactly what you code should do. It helps you write higher quality code (since being loosely coupled helps with testing) and it helps you write just enough code to make your tests pass (since your tests are in effect your specification).

2) The second value of unit tests is that if they are properly written they are very very fast. If I make a change to a class in your project can I run all the corresponding tests to see if I broke anything? How do I know which tests to run? And how long will they take? I can guarantee it will be longer than well written unit tests. You should be able to run all of you unit tests in a couple of minutes at the most.

查看更多
来,给爷笑一个
5楼-- · 2019-01-21 06:36

As many have mentioned, integration tests will tell you whether your system works, and unit tests will tell you where it doesn't. Strictly from a testing perspective, these two kinds of tests complement each other.

I can't see how adding a bunch more unit tests on individual classes would help. Wouldn't that make it harder to refactor as it's much more likely we will need to throw away and re-write tests?

No. It will make refactoring easier and better, and make it clearer to see what refactorings are appropriate and relevant. This is why we say that TDD is about design, not about testing. It's quite common for me to write a test for one method and in figuring out how to express what that method's result should be to come up with a very simple implementation in terms of some other method of the class under test. That implementation frequently finds its way into the class under test. Simpler, more solid implementations, cleaner boundaries, smaller methods: TDD - unit tests, specifically - lead you in this direction, and integration tests do not. They're both important, both useful, but they serve different purposes.

Yes, you may find yourself modifying and deleting unit tests on occasion to accommodate refactorings; that's fine, but it's not hard. And having those unit tests - and going through the experience of writing them - gives you better insight into your code, and better design.

查看更多
爱情/是我丢掉的垃圾
6楼-- · 2019-01-21 06:36

You might be interested in this question and the related answers too. You can find my addition to the answers that were already given here there (is this correct english? :)

查看更多
Animai°情兽
7楼-- · 2019-01-21 06:38

Joel Spolsky has written very interesting article about unit-testing (it was dialog between Joel and some other guy).

The main idea was that unit tests is very good thing but only if you use them in "limited" quantity. Joel doesn't recommend to achive state when 100% of your code is under testcases.

The problem with unit tests is that when you want to change architecture of your application you'll have to change all corresponding unit tests. And it'll take very much time (maybe even more time than the refactoring itself). And after all that work only few tests will fail.

So, write tests only for code that really can make some troubles.

How I use unit tests: I don't like TDD so I first write code then I test it (using console or browser) just to be sure that this code do nessecary work. And only after that I add "tricky" tests - 50% of them fail after first testing.

It works and it doesn't take much time.

查看更多
登录 后发表回答