What are the pros and cons of automated Unit Tests vs automated Integration tests?

前端 未结 12 1900
遥遥无期
遥遥无期 2020-12-12 20:35

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

What we have

The majority of these tests are integration tes

相关标签:
12条回答
  • 2020-12-12 21:05

    Unit tests execute methods in a class to verify proper input/output without testing the class in the larger context of your application. You might use mocks to simulate dependent classes -- you're doing black box testing of the class as a stand alone entity. Unit tests should be runnable from a developer workstation without any external service or software requirements.

    Integration tests will include other components of your application and third party software (your Oracle dev database, for example, or Selenium tests for a webapp). These tests might still be very fast and run as part of a continuous build, but because they inject additional dependencies they also risk injecting new bugs that cause problems for your code but are not caused by your code. Preferably, integration tests are also where you inject real/recorded data and assert that the application stack as a whole is behaving as expected given those inputs.

    The question comes down to what kind of bugs you're looking to find and how quickly you hope to find them. Unit tests help to reduce the number of "simple" mistakes while integration tests help you ferret out architectural and integration issues, hopefully simulating the effects of Murphy's Law on your application as a whole.

    0 讨论(0)
  • 2020-12-12 21:07

    We have 4 different types of tests in our project:

    1. Unit tests with mocking where necessary
    2. DB tests that act similar to unit tests but touch db & clean up afterwards
    3. Our logic is exposed through REST, so we have tests that do HTTP
    4. Webapp tests using WatiN that actually use IE instance and go over major functionality

    I like unit tests. They run really fast (100-1000x faster than #4 tests). They are type safe, so refactoring is quite easy (with good IDE).

    Main problem is how much work is required to do them properly. You have to mock everything: Db access, network access, other components. You have to decorate unmockable classes, getting a zillion mostly useless classes. You have to use DI so that your components are not tightly coupled and therefore not testable (note that using DI is not actually a downside :)

    I like tests #2. They do use the database and will report database errors, constraint violations and invalid columns. I think we get valuable testing using this.

    #3 and especially #4 are more problematic. They require some subset of production environment on build server. You have to build, deploy and have the app running. You have to have a clean DB every time. But in the end, it pays off. Watin tests require constant work, but you also get constant testing. We run tests on every commit and it is very easy to see when we break something.

    So, back to your question. Unit tests are fast (which is very important, build time should be less than, say, 10 minutes) and the are easy to refactor. Much easier than rewriting whole watin thing if your design changes. If you use a nice editor with good find usages command (e.g. IDEA or VS.NET + Resharper), you can always find where your code is being tested.

    With REST/HTTP tests, you get a good a good validation that your system actually works. But tests are slow to run, so it is hard to have a complete validation at this level. I assume your methods accept multiple parametres or possibly XML input. To check each node in XML or each parameter, it would take tens or hundreds of calls. You can do that with unit tests, but you cannot do that with REST calls, when each can take a big fraction of a second.

    Our unit tests check special boundary conditions far more often than #3 tests. They (#3) check that main functionality is working and that's it. This seems to work pretty well for us.

    0 讨论(0)
  • 2020-12-12 21:08

    You are asking pros and cons of two different things (what are the pros and cons of riding a horse vs riding a motorcycle?)

    Of course both are "automated tests" (~riding) but that doesn't mean that they are alternative (you don't ride a horse for hundreds of miles, and you don't ride a motorcycle in closed-to-vehicle muddy places)


    Unit Tests test the smallest unit of the code, usually a method. Each unit test is closely tied to the method it is testing, and if it's well written it's tied (almost) only with that.

    They are great to guide the design of new code and the refactoring of existing code. They are great to spot problems long before the system is ready for integration tests. Note that I wrote guide and all the Test Driven Development is about this word.

    It does not make any sense to have manual Unit Tests.

    What about refactoring, which seems to be your main concern? If you are refactoring just the implementation (content) of a method, but not its existence or "external behavior", the Unit Test is still valid and incredibly useful (you cannot imagine how much useful until you try).

    If you are refactoring more aggressively, changing methods existence or behavior, then yes, you need to write a new Unit Test for each new method, and possibly throw away the old one. But writing the Unit Test, especially if you write it before the code itself, will help to clarify the design (i.e. what the method should do, and what it shouldn't) without being confused by the implementation details (i.e. how the method should do the thing that it needs to do).


    Automated Integration Tests test the biggest unit of the code, usually the entire application.

    They are great to test use cases which you don't want to test by hand. But you can also have manual Integration Tests, and they are as effective (only less convenient).


    Starting a new project today, it does not make any sense not to have Unit Tests, but I'd say that for an existing project like yours it does not make too much sense to write them for everything you already have and it's working.

    In your case, I'd rather use a "middle ground" approach writing:

    1. smaller Integration Tests which only test the sections you are going to refactor. If you are refactoring the whole thing, then you can use your current Integration Tests, but if you are refactoring only -say- the XML generation, it does not make any sense to require the presence of the database, so I'd write a simple and small XML Integration Test.
    2. a bunch of Unit Tests for the new code you are going to write. As I already wrote above, Unit Tests will be ready as soon as you "mess with anything in between", making sure that your "mess" is going somewhere.

    In fact your Integration Test will only make sure that your "mess" is not working (because at the beginning it will not work, right?) but it will not give you any clue on

    • why it is not working
    • if your debugging of the "mess" is really fixing something
    • if your debugging of the "mess" is breaking something else

    Integration Tests will only give the confirmation at the end if the whole change was successful (and the answer will be "no" for a long time). The Integration Tests will not give you any help during the refactoring itself, which will make it harder and possibly frustrating. You need Unit Tests for that.

    0 讨论(0)
  • 2020-12-12 21:08

    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.

    0 讨论(0)
  • 2020-12-12 21:09

    Unit tests localize failures more tightly. Integration-level tests more closely correspond to user requirements and so are better predictor of delivery success. Neither of them is much good unless built and maintained, but both of them are very valuable if properly used.


    (more...)

    The thing with units tests is that no integration level test can exercise all the code as much as a good set of unit tests can. Yes, that can mean that you have to refactor the tests somewhat, but in general your tests shouldn't depend on the internals so much. So, lets say for example that you have a single function to get a power of two. You describe it (as a formal methods guy, I'd claim you specify it)

    long pow2(int p); // returns 2^p for 0 <= p <= 30
    

    Your test and your spec look essentially the same (this is sort of pseudo-xUnit for illustration):

    assertEqual(1073741824,pow2(30);
    assertEqual(1, pow2(0));
    assertException(domainError, pow2(-1));
    assertException(domainError, pow2(31));
    

    Now your implementation can be a for loop with a multiple, and you can come along later and change that to a shift.

    If you change the implementation so that, say, it's returning 16 bits (remember that sizeof(long) is only guaranteed to be no less than sizeof(short)) then this tests will fail quickly. An integration-level test should probably fail, but not certainly, and it's just as likely as not to fail somewhere far downstream of the computation of pow2(28).

    The point is that they really test for diferent situations. If you could build sufficiently details and extensive integration tests, you might be able to get the same level of coverage and degree of fine-grained testing, but it's probably hard to do at best, and the exponential state-space explosion will defeat you. By partitioning the state space using unit tests, the number of tests you need grows much less than exponentially.

    0 讨论(0)
  • 2020-12-12 21:11

    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.

    0 讨论(0)
提交回复
热议问题