I'm back in Java-land these days, which is culturally very pro-unit testing. After getting exposed to it again for a few months again I've come to side with the author here. I've never really been comfortable with the amount of time certain people dedicate to unit testing, especially the TDD crowd, but in my hiatus something has arisen in popularity which has made it all the worse: mockito.
Prior to mockito, unit testing was (more or less) limited to testing that your methods behaved as expected, and would occasionally expose NullPointerExceptions or other exceptional conditions. Dependent objects were either simplified or simply ignored. With the rise of mock object frameworks, however, your tests specifically say "this method on this mock will be called X number of times, with this result". Mind you, this is all happening in the context of another method call. So, for example, if you were testing the method "calculateDueDate", and that method took a DateTime object, your test might look like this:
@Before
public void setup() {
MyClass myClass = new MyClass();
DateTime mockDateTime = mock(DateTime.class);
}
@Test
public void specificDueDateShouldBeTenDaysFromNow() {
DateTime result = myClass.calculateDueDate(mockDateTime);
verify(mockDateTime, times(2)).getHour(); // contrived
}
The problem with this is that the tests become obstacles in the way of refactoring the code. Should you decide that you don't want to use the DateTime library any longer you will have to not just change the code which is using it but the tests as well. Or what if, going back to the example above, you decided not to use the getHour() method any more? Every test referencing that will have to be changed. And changing those tests is very likely to be more involved than changing the code under test, because there frequently are more tests than code. This has a negative impact on the design of the application. Because companies rarely dedicate resources to making existing software better purely for its own sake, you tend to have to do what you can when you can. This means your time is limited to make that refactor, or upgrade that library, or do whatevever change it is that needs to be done. Unit tests, especially those that use mocks, can get in the way of this to such an extent as to make such efforts impossible.
I think testing is important. I do not, however, share the belief that is the sole, or even primary, determinant of code quality. In fact, an over reliance on unit testing can easily be a net negative. Should unit tests be thrown out? No. Baby with the bathwater and all that. But they should not be viewed as a silver bullet, either. They're not. They can help, but they can hurt.
I've had two experiences with unit testing recently that have made me a believer.
One of them was that I was working on a team where a programmer quit and I had to get a very complex codebase ready for production. The last programmer was terrible, the kind of guy who had trouble making primary keys that were unique, where any code that could possibly have a race condition did, and so forth. The code had unit tests, however, and that made it salvagable, and eventually I got the system to a place where it worked correctly and the customers loved it.
In nine months of effort on this, I ran into one refactoring where it felt the unit tests were a burden, and that involved about a day of work rewriting the tests. Unit tests are more likely to be a problem, however, when they add to the time of the build process. For instance, I wrote something in JUnit that hammered part of the system for race conditions, and this was key to fixing races in that part of the system. It fired off a thousand threads and took two minutes to run, and adding two minutes to your build is a BIG PROBLEM, particularly if anybody who wants to add two minutes to your build can do so and if anybody who wants to remove two minutes from the build is called "a complainer" and "not a team player". Overall the CPU time it takes to run is more likely to be a problem than the developer time it takes to maintain them.
As for Mockito I have found it is a great help for writing Map/Reduce jobs. As I don't own a big cluster and as I sometimes like to code on the run with my laptop, an integration test typically takes ten minutes with Amazon Elastic Map/Reduce. It takes some time to code up tests, but I get it all back with dividends because often I get jobs running with two or three integration test cycles instead of ten or twenty. When I find problems in the integration tests, usually I can reproduce them in the unit tests and solve them there.
Now, it did take considerable investment to get to the point where unit testing worked so well for me. I used to have problems where "the tests worked" but the real application didn't because Hadoop reuses Writable objects so if you just pass a List of objects to the reducer, you might get different results in a test than you do in real life. Creating an Iterable object that behaves more like Hadoop does solved that problem.
Generally if you are feeling that "unit testing sucks" or "mockito sucks" it's often that case that you're not doing it the right way.
My sense is that one should make the unit tests to be as resilient as possible to refactoring and changes. This means that for so long as the public behavior of the class does not change, one should not need to do much if any updates to the tests.
Thus any test that is written in such a way that it would be present an issue in refactoring code should be avoided if at all possible. A simple example is directly constructing the class under test in the test method:
@Test
public void tryToAvoidDoingThis() {
MyClass = new MyClass(param1, param2);
// do stuff to my class
}
If this is done for each test method, when the constructor parameters change, e.g. a new one is added, then each of the constructors calls in the test method(s) will have to be updated.
Instead, have a level of indirection and have a single method that can create a sample MyClass. Now when the parameters change, only one construction site has to be updated.
In general, unit tests should not be testing specific / internal implementation details of the class. Rather, the tests should verify the documented public behavior of the class.
There's an inconsistency here: unit tests should depend only on the public behavior of a class; the constructor is public; constructor calls should nevertheless be avoided where possible.
Factoring out a common constructor in tests is an example of making the tests resilient against changes in the underlying code. If the constructor changes, the tests need to be fixed in one place, not in 50.
Other examples may be around a `setup` method. If the method is private, don't test it. Then you can refactor freely. If it's public, test the pre/post conditions around the method in as few places as possible (hopefully one). Even if other tests rely on the object having been "setup", just trust that it works. If the specification of `setup` changes, you just have the `setup` tests to update, not the entire object.
Like all process you have to do what works for you. First, to steal from the recent airbnb article, the bar for testing has to be so low you trip over it. The testing framework should make it easy to get down to writing tests.
Second, start writing tests to verify bug reports and then fix the bug. In large systems I find this critical to honing in on the exact problem. The mental exercise of crafting the test to trigger the bug helps me really understand the problem.
Finally, start new features by first writing a test to simply drive your new codes golden path. When working in a large system I find writing a test to run my new code a much faster development turn around time than rebooting the entire system. This is compounded when there are many systems or moving parts which is common today.
> Well explain further. I hate these smart arse sounding comments - "you are doing it wrong" without any indication why, or how to do it better.
With unit tests, there are certain things that must be tested, such as very high-value code contracts, and the like. There are many things that people test (like "correct output" for the input) which may not be so valuable, particularly if several possible values may be correct.
So test contracts, not internals, and not representation. And please for the heaven's sake, don't test the behavior of your dependencies.
Unfortunately I also believe those comments are generally true, and I also believe when the posters answer "why", they will give you an answer that is also doing it the wrong way.
I have no idea how to do unit testing. I only believe there is a right way.
By reading the unit tests and code comments, and comparing them against bits of the actual codebase, you can gain a better understanding of what the previous programmer was thinking and what he was trying to accomplish.
That's the most obvious benefit of having tests for me. Documentation can be outdated and even if it isn't it very rarely contains examples of use. Passing tests are for me exactly that. An up to date example of how the thing should be used and what can I expect from it.
For better or worse, this system had a number of data-centric objects; these objects passed many correct tests, but they failed to be deserializable from XML (because of the way collections were handled) as well as having other deficiencies.
The tests meant I could fix those deficiencies quickly and have faith that I fixed them correctly. Of course, I added new tests to test that the system did the things it had to do.
So... Unit Tess are good because, if you take over a code base written by someone who is incompetent, and if that person wrote terrible unit tests which should have been failing (but are passing, due to said incompetence), you have slightly more information to work with when fixing the original code? Seems like a weak argument to me.
Sure, but it's hardly a reason to write unit tests.
I write unit tests for every one of my core algorithms. I have also seen a massive amount of dumb tests written by TDD people who strive for 100% code coverage. Ridiculous. It takes as much time to maintain the (often useless) tests as it does the code. Where they make sense they're indispensable.
Agreed. It's not a reason to dismiss tests though.
I think the fundamental question is why 100% code coverage is important. The fact is that it isn't. the problem with TDD is that a lot of people who do it totally get the idea of why you are testing entirely wrong. The goal should never be to "ensure your code functions properly." The coal should be to "ensure the code contracts are adhered to." If you test with that in mind, you will write lots of unit tests and almost never have to rewrite or delete them due to fixing bugs.
Again this comes back to what I said in another comment that tests should never be written to the code. Once you get that, then code coverage ends up being meaningless and not something you want to worry about.
This is probably not best practice, but I often disable tests that take a really long time. An alternative would be to have a 'full suite' and a 'fast suite'. The fast subset could be used locally for most development, but then you run the full superset when you are ready to release. A 5 minute release is no big deal, but if it takes 5 minutes to run a standard dev test, then people are not going to test as much.
I also disable tests by default that require a working installation to run. This allows me to have a test suite that can be run prior to installation and a larger support test suite that can pinpoint problems on production systems.
It is ok to have one fast suit of tests that are run all the time and one slow but more detailed that runs only at some checkpoints (overnight, weekly, before release).
Big projects over some size normally do it that way.
If you've got a slow test suite, that mean's you've got a bad test suite. Taking away tests from it to make it faster takes away from its purpose, which is to aid you in refactoring.
Or it means you are testing something that's computationally expensive. Not everything is just web model input validation--some people are doing real work. :P
Not nessasarily. I've worked on math heavy programs where single calculations could take seconds to run. For the frequency they come up in actual use, this was not a problem, but our tests needed to run these calculations more than any single execution of the program would likely need to.
More specifically, consider an SSE2-based function 'float32 floor(float32)'. There's only about 4 billion inputs, so why not test them all? That only takes a minute or so.
How is testing 100 inputs a unit test and testing 4 billion inputs, through exactly the same API, an integration test?
As the author points out, many people wrote libraries which are supposed to handle the entire range, but ended up making errors under various conditions, and even given wrong answers for over 20% of the possible input range.
Is 90 seconds to test a function "slow"? What about 4.5 minutes to test three functions?
If you say it's slow then either it's a bad test suite, and/or it includes integration tests. I believe that is the logic, yes?
There is no lower unit to test, so therefore this must be a unit test.
The linked-to page shows that testing all possibilities identifies flaws that normal manual test construction did not find. Therefore, it must be a better test suite than using manually selected test cases, with several examples of poorly tested implementations.
(Note: writing an exact test against the equivalent libc results is easier to write than selecting test cases manually, and it's easier for someone else to verify that the code is testing all possibilities than to verify that a selected set of corner cases is complete.)
Therefore, logic says that it is not a bad test suite.
Since it contains unit tests and it is not a bad test suite, therefore it must not be slow.
Therefore, 4.5 minutes to unit test these three functions is not "slow".
Therefore, acceptable unit tests may take several minutes to run.
That is what the logic says. Do you agree? If not, where is the flaw in my logic?
How can you have a good test suite without integration tests? That's not a full test suite. That's a cop-out.
A good test suite has two qualities - how comprehensive it is and how fast it takes to run. If either is lacking, then it is no longer a good test suite.
It's quite easy to have slow tests that aren't integration tests. For instance, there's some tests in Sympy that are only a few lines of code that run very slow because the calculation is difficult. Sometimes (but not always), it's trying to calculate a very difficult integral (which is a test of integration, but not an integration test).
Or it just means you have tests which could be better optimized for speed but in fact are optimized for something else.
We had a series of tests (more towards integration tests I guess) at one point in LedgerSMB that did things like check database permissions for some semblance of sanity. These took about 10 min to run on a decent system. The reason was we stuck with functionality we could guarantee wouldn't change (information schema) which did not perform well in this case. Eventually we got tired of this and rewrote the tests against the system tables cutting it down to something quite manageable.
We had this test mixed in with db logic unit tests because it provided more information we could use to track other failures of tests (i.e. "the database is sanely set up" is a prerequisite for db unit tests).
Heavy computation algorithms. My main focus is on geospatial analysis, and to test certain things, you are going to end up with some 1000ms+ tests. Get 10 or 20 of those, and you have a problem.
> Generally if you are feeling that "unit testing sucks" or "mockito sucks" it's often that case that you're not doing it the right way.
Either that, or the person just hasn't been sufficiently burned by someone changing something you're not aware of and having to track down a run time error for days that could have been caught and fixed by a unit test in minutes.
I read the article, and much of what he speaks of is tautological unit tests - testing something where someone could never have done anything but that. I've seen people unit test the behavior of filling up a collection then test to make sure the collection has all of those elements, for instance. And there, he has a point.
But there's a dangerous line there. And while the article makes a good point that too much of that type of testing can be detrimental, I've generally found it better to err on the safer side of more tests.
On your own project, or even on a small team, you can probably get away without them much of the time. But, on larger projects, where many different developers sometimes go back and make changes to code they didn't write, it's very easy for a new guy/gal to make changes with unanticipated consequences. When that occurs without sufficient test coverage, the project will wind up spending 10-20x more man hours to fix the issue.
Different between unit tests and integration tests. Individual unit tests should be on the order of a millisecond or less so you can rip through them very quickly. If you use Surefire and Maven, name tests with the suffix ITCase (can override) and then you can either run the unit tests or the integration tests with mvn test or mvn integration-test.
http://randomascii.wordpress.com/2014/01/27/theres-only-four... tested all 4+ billion inputs to ceil(), floor(), and round(), and pointed out that many libraries actually had high error rates, because of incorrect support for rounding, small values, NaN, and -0.0.
Each test is extremely fast. Testing all 12+ billion cases takes 4.5 minutes. Are these unit tests or integration tests?
If they are integration tests, roughly where is the boundary between the two?
Is it meaningful to distinguish between "unit" and "integration" testing based only on the amount of time they takes? That is, if the unit tests take 0.1 second too long then do they suddenly become integration tests?
I think the problem is that definitions that should reflect the granularity of what is being tested have become too interconnected with assumptions about frequency of testing / time to run and it has created completely mangled definitions.
The problem is so bad that some developers I've crossed have the firm opinion that mstest should only be used for "unit" tests. It is frustrating.
If you have slow unit tests, don't run them on every compile. If you have fast integration tests, run them as much as you'd like. I've personally never defined unit tests as "things that run fast", despite that being a valuable property it does not seem essential to the definition, but perhaps I have the wrong understanding of what a unit test is.
12B test permutations is not your typical scenario, though 4min is pretty damn quick for all that. I'm asserting that for a given project module, it is beneficial to be able to run the test suite in a short time, say 10-15s. If you've got to wait minutes, then it is more integration.
The longer your unit tests take, the less likely people will be to use and run them often, which is the whole point. Let the nightly build on the CI machine exercise the long running tests when everyone is asleep.
True. Only a few tests are fast enough to run 12B tests within a few minutes.
Really, I think the problem is that unit test frameworks are currently incapable of doing the right testing.
Unit tests take quadratic time. That is, each new test requires running all previous tests, to get the green. And at some point, a project will have enough tests that it can't finish in 10-15s.
One option is to mark "fast" and "slow" tests. Another is to recategorize them as "unit" vs. "functional" tests.
These are poorly-defined labels. In this case, the 4.5 minutes of testing is "slow", yes, but it only needs to be run when a specific, small part of the code changes. The problem is, there's no way to determine that automatically. The test runner can't look at the previous test execution path and see that nothing has changed, and there's no way to mark that a test should only be run if code in functions X, Y, or Z of module ABC has changed.
Humans are able to figure this out. Well, sometimes. And with lots of mistakes. Get the unit test framework to talk with a coverage analysis tool, plus some static analysis and perhaps a few annotations, and this discussion of how to distinguish one set of tests from another disappears.
Blue-sky dreams. I know. :)
In real life we toss those functions into their own library, note that the code is static, and do the full test suite only occasionally; mostly when the compiler changes.
In other words, bypass CI the same way one does any other third party library. (How often do you run the gcc test suite?)
Various test runners do just this. Maven on TeamCity ranks the tests by their volatility (recently failed first), then by run duration. The point is to run the most likely to fail and historically most brittle tests first and the slow stuff last so you can fail fast.
That still means to run all the tests each time, with re-prioritization to enrich the likelihood of faster feedback.
But if none of the code paths used for a test have changed, and the compiler hasn't changed, and there's nothing which depends on random input or timing effects, then why run those tests at all?
The reason is we don't have a good way to do that dependency analysis, which is why we run all of the tests all of the time. Or we manually partition them into "slow" and "fast" tests.
Code instrumented for coverage tells you which tests executed which portions of code. As I remember, Google's C++ build/test system was using this by late 2009 to efficiently run all tests on all checkins to HEAD.
> Individual unit tests should be on the order of a millisecond or less so you can rip through them very quickly.
So now we have to write tests for all our code, and tests that run fast. We have to refactor our code around the tests. Something seems a bit back to front here. Or is the assumption that if we have 100% test coverage that runs fast then it means that we have written the best code possible?
I think I am siding with the author of the article on this one.
I find when I have to re-factor code around the tests it means the code wasn't very good in the first place.
The author complains about re-factoring code into smaller testable functions. I completely disagree. Code structured as small easily understood functions which do one thing and have obvious inputs and outputs is good code which is much easier to extend and modify.
Yeah, that's one of the article's weaker spots. But as a rule, I'd tend to interpret imprecise statements like that charitably. He's not saying that small, clear functions are bad, but that splitting functions for the purposes of testing is counterproductive. He's not saying anything about splitting for clarity and focus.
Note that the original article doesn't disavow all unit tests; nor does it disavow all testing.
Sounds to me like the kind of tests you're describing aren't necessarily unit tests (system-level race conditions aren't typically discoverable with a simple unit test); where the tests were truly unit-level, the proposed alternative (assertions) may have been even more informative. Finally - a few real unit tests for known correct behavior are advised where in essence the algorithm can be described independently from its outcome.
I think the only verification that should be done with mocks (the verify method) is that important collaborators have been called. Things that run off and mutate some global state, or perform an action.
Stubs are useful for controlling the execution state of the method under test. You could stub DateTime such that it was midnight in one test, and noon in another, if the behavior of the function relied on that. It does introduce a dependency that has to be changed along with the code, but is often helpful.
I do agree with the author that there's an art to testing appropriately. You could mechanically write testing code for every line of code under test (if statement with single condition? Two tests to verify each path is taken!), which leads to your app being coded twice: once in code and once (or more) in the tests.
The only thing mocks are good for IMHO is when the real code takes too long to run. Actually testing that a function is called twice is not what you're testing and this level of coupling to the supposedly encapsulated behavior of the method is ridiculous. The method is not called "callMethodInClassFooTwice()" it's called calculateInterestRate() or whatever and as long as it does that right that's all that's important.
Calls to external services and hardware, or really, any external system. It's probably quite important that your code interacts properly with the system, but you also probably don't want to interact with it every time you run the tests. Not unless you have a copy of the external system running for every developer.
> It does introduce a dependency that has to be changed along with the code, but is often helpful.
And that's the rub. Mocks are useful. They help you write tests that are more robust, without having to resort to massive and complex @Befores. At the same time, though, they have an effect that is more negative than positive on the ability to change the code under test.
Well I know it's a contrived example, but I don't understand the motivation behind mocking an external library's code. That library should have its own tests.
Say I have three layers of custom code: A calls B, which calls C.
If I want to test B, then I want to mock C, and have my test call B similar to how A does. I want to mock C because C is also custom, and if my test fails, I want to know if it's because of bad B implementation, and not because a buggy C might be confusing matters.
But if B also calls D from an external open source or vendor-supplied library, I don't usually want to mock D. That just adds needless complexity to the test, and reduces my focus on my own custom code.
An exception would be if this library code makes its own network call or something - then you might want to mock it to save time.
Anyway, mocked unit tests become far simpler if you maintain the right focus. Use test A to call B (passing in canned fixtures if necessary), while B mocks C only to maintain focus on B's implementation. If you start getting involved in trying to mock external library code, or even internal private methods that B calls, you'll have a bad time.
The advantage to maintaining that kind of focus is that refactoring becomes easier. Want to change the name of C? Your IDE should handle refactoring your test, too. Want to change the implementation of B? You don't even need to change your test at all, just make sure the right values are still there in the return value. Maybe you'd need to add a couple of assertions, but that's it. If you're looking at having to do a serious refactoring of your unit test in those cases, then it probably just means you're still designing your code architecture and things are still really fluid. And in that case, it would make sense that you might have to throw away your test, because by definition it means you are still deciding on what your specifications are.
The guys who came up with the mock object approach to TDD would say that you shouldn't be mocking external libraries directly. You want your own time abstraction which is probably far simpler than what you get from a library that has to satisfy everyone's needs.
I think that building that level of isolation between you and your framework or library is just basic good practice. The fact that you need time doesn't change, but the way that you get it might.
I'd rather have a program that does what it does in X lines of code, than a unit tested, mocked, codebase in 5X lines of code. Sure you have tests, for whatever they're worth (I'm somewhat skeptical of TDD in the first place), but you have so much MORE code.
It's just basic separation of concerns. I've seen too many development organizations brought to their knees by the fact that they don't have any layering between their logic and the libraries/frameworks they use. It's a very real problem.
That's interesting. I don't have much experience with it, but when I've seen similar stuff it looked like an anti-pattern to me. Why should developers need to learn your specific wrapper on top of a popular 3rd party component? The internal thing is most likely not documented as well, and common problems don't have answers on Stack Overflow. It requires extra work to use additional features of the library.
I'm similarly wary of convenience libraries that provide marginally simpler APIs on top of standard libraries.
I'm not convinced it's a good idea. I wish I had your experience. Any good reading material?
Wrapping a library in your own concept allows you to define what's right for your application. It has the effect of pushing the third party library out to the edges of your system, replaced with whatever you wrapped it with. This makes replacing it, for testing or any other reason, much easier than if it's proliferated throughout your code unwrapped.
Wrappers should be simple so creating them and documentation, beyond a few integration tests to understand how the library works, shouldn't be a huge concern.
I've been doing a lot of Javascript stuff lately and libraries like Dojo have 5 different ways to locate an element in the DOM. I have no idea if all these ways will be around in a future release, or, right now, which one is better. Unifying the Dom select code behind our own interface keeps things uniform throughout the app, instead of each of us using a different function, and lets us try out the different library functions, or different libraries, easily.
But where design comes into place is to determine where and when you need to separate these concerns. There are always times to do this. There are also times not to.
For example (Perl example here), in LedgerSMB we layer some things. We layer the templating engine. We layer (now, for 1.4) arbitrary precision floats. We layer datetime objects. Many of these are layered in such a way that they are transparent to most of the code.
But there are a lot of things that aren't layered because there isn't a clear case for so doing right now.
(As a footnote, PGObject requires that applications layer the underlying framework because there are certain decisions we don't feel comfortable making for the developer, such as database connection management.)
I agree. Wrapping your library for testing is really pushing the boundaries of sensibility.
Low density code that doesn't provide application logic is one of my pet peeves.
There is a philosophy that started in the 90s (and Microsoft was proponent of it [1]) that adding more layers to an application would make it more malleable, but in a typical CRUD web app, layers only bloat the code and make it slower.
I'd suspect adding a wrapper to a date class just for the sake of testing is more likely to add bugs than remove them.
Not exactly the same case, but it sure is nice to be independent of calling System.getCurrentTimeMillis() in the code under test. One example of how to do it (in a simple case): "TDD, Unit Tests and the Passage of Time" http://henrikwarne.com/2013/12/08/tdd-unit-tests-and-the-pas...
That sounds more like an argument for putting an interface in front of an implementation on the app side, and then mocking that interface in the test. Which is totally fine, because then you are isolating the custom implementation (a light shell to the external library). As opposed to mocking the external implementation in the test.
That's more along the lines of what a lot of TDD literature says.
Write adapters or facades that wrap external libraries, use those in your own code's unit tests. This makes you less bound to a specific library as well. Don't mock the outside world [0] for testing your adapters/facades/whatever, but do integration tests that cover your adapters using the real outside world.
You'd also do complete end to end tests where the entire system is used as if it were in production (acceptance testing). TDD makes a lot more sense if you think of it in those three layers: acceptance, integration, unit.
[0] Outside world means anything that's not your code -- networking, filesystem, external libraries, etc.
<i> Should you decide that you don't want to use the DateTime library any longer you will have to not just change the code which is using it but the tests as well. </i>
This is not a bad thing. Ostensibly, you're making a change to code which implements a business function for a reason. If you're not using DateTime anymore, there should be a reason why.
Tests are supposed to be the proof that a business function is performed correctly.
If date/time is such a fundamental piece of logic that you have many tests that verify various conditions based on what day or time it is, and you change the fundamental WAY you represent date and time, then why is it bad that you have to make a lot of test changes as well as code changes?
Having those tests, and changing them over should be worth the effort to give you the confidence that your new implementation of how a fundamental of your code works the same way as before.
As far as the concept of checking "this method on this collaborator will be called X times," that can be painful to get correct the first time, but say you again change business logic and add a new condition. Is it not worth the effort to either think about "Oh, now I will have to call getHour one more time" and fix the test, or discover a broken test AND CONSIDER WHY you're being told "expected 2 calls but got 3" ?
Granted, if you're just changing times(2) to times(3) and going on, well, then don't bother. But that SHOULD be telling you something valuable. It's up to individual developers whether they choose to see that value or not.
Business logic doesn't require that you use DateTime. Business logic requires that the software understand dates in a certain way.
Some unit test styles enforce a given API at all levels in the code. Others restrict themselves to tests through certain restricted APIs.
For example, the SQLite code tests through the API, so it's possible to do major revisions of the code without changing the tests. The choice of how date logic works is invisible to the tests. Date tests can be done through SQL, without testing the actual code functionally equivalent to DateTime.
I prefer the latter. I liken it to building a bridge. The main test is "can you safely get traffic from X to Y", with other tests related to maintenance, beauty, environmental impact, and so on. This can start with a stone bridge, then a truss bridge, or a suspension bridge.
It's also possible to have tests like "there must be a pillar here, a truss there, an arch up there, and a screw over there", which requires that the bridge must be an iron bridge using a Pratt truss design, with three support columns made of granite.
Rebuilding/refactoring to even, say, a Parker truss would require rewriting some of the tests. Rebuilding/refactoring to, say, a truss arch requires a lot of rewritten tests.
But under the high-level tests, like for SQLite, major redesigns don't require major changes in the tests.
If at the start you know that you want a specific bridge, and you just have to work on it, then go ahead and write these very specific tests. Just remember that major refactors will require a lot of rewritten tests.
There are cases where I do tests like that. There are two reasons for them.
1. It can save time debugging failures because I know whether the basic instantiation of thing was sane or not.
2. There are cases where there are complex object instantiation tests where an object is returned according to some other logic. Obviously these need to be tested.
In the first case those aren't tests for the sake of testing the API. They are sanity checks for saving time troubleshooting other test case failures.
That's what it is. You have a unified interface for generating things, and you need to have deterministic test to make sure your thing is being generated correctly and the data generates the thing fully and correctly.
As with all programming practices, theory and application do not always align, especially when the application achieves cargo cult status.
The details of how MyClass calculates due dates should be irrelevant. The test in your example violates this. It makes privileged assumptions to assert implementation details.
This is why it's better to code and test against interfaces. This makes it harder to make any assumptions about the implementation because it puts the abstraction front and center. A test suite written against an interface can be applied to multiple implementations. This of course requires some dependency orchestration to load different implementations into the suite, as well as to create/avail any needed mocks for that implementation (which ideally are also declared as interfaces). But this limits ripples from implementation changes to the dependency orchestration code.
With that in mind, if an implementation's internals need testing, then tests can be written against the class itself (rather than any interface). If the class has dependencies, then it should be obvious (or documented) how/when those dependencies will be accessed. When necessitated by the class's outward responsibilities, this makes it possible to ensure that a dependency is correctly accessed in certain conditions. E.g. Perhaps it must be ensured that an order processor always asks for validation from a credit card gateway. Your example does not meet this standard.
Basic theory aside, I personally agree that high coverage is not worthwhile. I prefer tests that ensure bug fixes don't regress, or that are high-level and mimic end-consumer behavior.
Just want to agree with your sentiment here. I have tried on many occasions to incorporate mocking frameworks into my unit tests, and in almost all cases the mocks cause more problems than they solve. Either I'm spending time debugging the buggy behavior of the mock objects themselves, or my test writing is slowed down with a bunch of tedious minutiae about the expectations of the mock framework. For whatever reason I have also found the mock-based tests to be very difficult to refactor when required.
I generally prefer to write a mix of unit and integration tests, and I typically find the integration tests to be more useful.
I don't see the point of asserting that getHour has been called in your (admittedly contrived) example. Mocking (or rather stubbing) a datetime library is usually just useful to get a predictable result. Making assertions on what methods get called and how many times seems useless in that case.
Sometimes it can be useful to make this kind of assertions on a mock though. Say you're mocking a web service, you can use this kind of assertion to ensure you're calling the web service properly. In this case it's useful because it's verifying behavior at a boundary between your program and the outside world. That's what I think tests are for. The outside world is not made only of users. Communication with external resources is also important and can be verified with mocks. In other words, this kind of mock assertions is interesting to verify the indirect outputs of the program: http://xunitpatterns.com/indirect%20output.html
Regarding Mockito specifically, I think there are worse and better ways to use it. I think it's great for mocking out external services, like web services calls or database queries. That way, you can easily test a bit of functionality without having to instantiate the universe.
On the other hand, I've also seen it used overly invasively -- that is, the test writer uses it to verify the status of some object internal to the class under test, like your example above. As you note, it creates an incredibly tight coupling between the test and the method being tested.
In general, I think that this sort of thing is correlated with classes that are highly stateful. If, for example, your class is full of void methods that modify class members, then you may have no other option than inspecting the internals of the class in order to test its functionality. OTOH, if you can manage to write things in a more functional, less state-heavy manner, then you need less of this sort of introspection.
Yes, mocking too much is a problem. Perhaps the greatest benefit of unit tests is that they force you to write code that can be tested in isolation. In other words, your code becomes modular. When you mock the whole world around your objects, there is nothing that forces you to separate the parts. You end up with code where you can’t create anything in isolation – it is all tangled together. From a recent tweet by Bill Wake: ”It’s ironic – the more powerful the mocking framework, the less pressure you feel to improve your design.” (from http://henrikwarne.com/2014/02/19/5-unit-testing-mistakes/)
> From a recent tweet by Bill Wake: ”It’s ironic – the more powerful the mocking framework, the less pressure you feel to improve your design.”
This is exactly my sentiment. I would go even further, though, and say that "the more powerful the mocking framework, the more it discourages design improvements." If you have 50 or 60 references in your test to a mocked class of type A, and A turns out to no longer be needed, then the effort to get rid of it is higher than it otherwise would be.
I think the fundamental problem is that unit testing is hard to do right. It needs to be a part of software design, not coding and you should never write your tests to your code (but everybody does this anyway).
Note, I am very much for unit tests. However, I recognize that the basic test cases really are software engineering rather than development territory. So I think this whole argument points at a much deeper problem, and one I don't think test driven development is the answer for.
let's draw a parallel to the Python midnight time as boolean discussion for a moment. The Python midnight time issue was entirely because the developer of the library made his code the contract. "This is how it works so hence live with what that means" is not really a contract and in that case it had some really nasty corner cases (timezones west of GMT never evaluate to false for any time with a timezone, but timezones UTC and east evaluate to false when the time represents UTC midnight). But a lot of people didn't want to break that contract. Really? That's a contract that could only be appreciated by tax lawyers.
The same fundamental problem happens with unit tests. "This code works so it must be correct" is a typical response and so the code becomes the contract. A bug is found, and fixed, and the test cases start failing. This is a waste of time. It is a waste of time to write the test. It is a waste of time to remove it because it is irrelevant.
My recommendation is to do extensive unit testing. However, take the unit tests seriously. Before you write a test, ask yourself "If a bug is found and fixed in the software or any of its dependencies, should this test ever fail?" If the answer is "yes" then don't write the test.
The point of unit tests are to ensure that software contracts are not mistakenly broken. This is true no matter how you write code. My test scripts are always longer code-wise than my production code. This is true for SQL and PL/PGSQL, just as it is for Perl. However, I try hard to ensure that a test failure means that a previously working API is no longer working rather than that I changed something.....
That just sounds like a terrible functional spec. If it's a core part of the behavior that some mocked object is called 11 times then so be it, and so mock it. You're not introducing brittleness via your tests—you're introducing it because your functional spec is highly complex.
Is it important that you're testing the implementation interactions with DateTime? Why not ensure for the supplied DateTime that the expected result is returned?
I run into the problems with date-bound data all the time. It took me awhile of doing it the hard way. Now, if I'm working with dates, I usually always have a 'DateCalculator' factory that injects dates into my domain objects. Now, it's easy to handle dates in my tests: just implement a DateCalculator as a mock.
But, as for TDD, I think people can go way, way overboard. There are serious diminishing returns to doing TDD 'the right way'. I'm quite satisfied using unit tests to test particularly complex business logic / domain logic and as integration tests. I find that all too often devs will get so enamored with their 100s of tests that basically test nothing (as a ratio of functionality to lines of code), that they'll forget the main goal: shipping relatively high-quality software.
Disclaimer: I started as a hack, worked for many years to become a non-hack, then devolved to a hack with caveats. Now, for me, it's all about achieving the right balance between speed and perfection. YMMV.
You've fallen into a common trap. A trap that many developers and managers do - that software is the goal.
It's not.
Software is a means to an end. A tool we built for users and customers to solve their problems.
Software that doesn't solve the customer's problems might as well be gravel on a beach.
Software that is shipped, but isn't maintained, will diverge from customer's needs as they change. And will become just as useless.
So are you trying to precisely understand a customer's key requirements? Then you best represent them in unit tests.
Are you building and releasing iteratively and getting customer feedback to make sure that customers are getting what they actually need? (A lot of times what people say they need changes once they actually get what they ask for) Then you need to be able to refactor frequently and fearlessly, and you best have tests.
Are you considering future releases after shipping major versions? Same, you will need to refactor, and you will break existing functionality, and you need tests.
I understand you want to be pragmatic. But don't think that the people that preach TDD are just sitting there in love with their best practices for their own sake. We have just been burned too many times and believe the best process and framework for delivering customer value via software is to build on a solid foundation.
On second thought, I think it's an interesting topic of conversation.
When I'm working, my primary goal is to make money. And, when I'm working for a client or boss or customer, that means being responsive and showing them value for _their_ money. Oftentimes, this means trying to protect them from themselves, but, again, I want to get paid. So, eventually, despite all my arguments, it turns back to me shipping software. They want to 'see' something for my expensive invoice, subscription fee, or salary. The payer doesn't care about all that 'stuff' that I do that they can't 'see'.
So, yes, you're right. I do sacrifice quality at times to ensure that I get paid. It's not intentional. No way. But, I am a biased participant in this debacle: I have a perverse incentive to ship non-perfect software. I don't intentionally try to do so, obviously. But, there is an equilibrium point where I stop arguing and see that I can fix it in v2.
Now, on my personal projects, I actually take my time. I do things the right way, because I have time on my side. I'm my own boss. And, in those cases, I actually do write full sets of unit tests.
Interesting for me to observe this in myself. Thanks.
But, yes, when I'm working for someone, I have considerable pressure to break the rules. You're right - it is a trap.
"the main goal: shipping software." There is a point, where people seem to forget that unit tests are a tool that have a place, and should be used appropriately. There is a point where it becomes more like a bureaucracy, or buying into a religion, where its not about using the tests to solve problems, but about checking boxes or doing it the "right" way.
I like tests. When a section of code is complex, and can't reasonably be refactored, a test is a good way to help build confidence that you've driven the wonkiness out of it, and don't reintroduce it with further changes. They help you catch dumb mistakes, where you change how you're passing a reference, or the type of child object a function sees, and suddenly, you're getting subtly wrong output. I do a lot of Computational Fluid Mechanics (CFD), and we've got a standard suite we run when we compile to make sure new changes haven't broken our base functionality.
Still, in most cases, I'd much rather look for a refactoring or functionality combination option. I'd rather each piece be small enough that I can hold a mental model of it in my head, and not be too scared I'm missing something. However, also still a bit of a hack.
I'm personally of the opinion that verify is harmful, because it does tie your test to your implementation. Unless that's absolutely needed (it probably isn't), it's a bad idea.
I use mocks more-or-less as advanced stubs, because when something has 600 methods in it (legacy code base fun) and I need one of them I don't particularly feel like generating that entire stub.
This is not a good example, because in this example, Mockito is used to mock a very simple class (DateTime), which should not be mocked at all. It is also used to verify that the dependent object is called exactly twice, which may be unnecessary.
This is more of a problem where a library is not used well. Mockito is a very good library, this example really does it disservice.
Mockito easily leads to a false belief that code is tested. Mocking dependencies and simply asserting those mocks is useless yet I see that all the time.
Mocks are useful for providing state where it's queried from external objects. They are also great to verify that the intended external side effects of an action occur. verify isn't / unit tests aren't the right mechanism for forcing a particular implementation (which is what your example test does - why do you care if getHour is called, it doesn't update state in another part of the system?) Your tests should almost never never say "this method on this mock will be called X number of times with this result", unless it's something that you actually care about (in which case it should be the entire subject of the test).
The above test is much easier without mocks. You construct a date, pass it in, then check that the result is equal to the input plus 10 days. That's all you care about, so that's all you test.
Verify is useful if you have to update external state. If, in your contrived example, for whatever reason, you needed to keep a tally of all of the times that due dates were ever calculated and that was stored somewhere else, you might have a test that mocks your CalculationCounterClass and verifies that a method (addCalculatedDueDate or whatever) gets called on there...
Good, simple (and IMO brittle) tests that confirm what you care about are useful. Forcing a particular implementation through your tests is a drain.
The problem with this is that the tests become obstacles in the way of refactoring the code.
I'm not a TDD stickler or anything, but tests are what make refactors even possible. Will tests catch all the issues? No, but they will make sure the part you refactored still interacts properly through its public interface.
If some tests have to be changed that is even better because those tests hopefully contain corner cases that have been caught and tested for over the life of the original code, and will force the person doing the refactor to think about those issues.
I'm not a TDD stickler or anything, but tests are what make refactors even possible.
Perhaps in general tests provide a useful safety net for refactoring, but I think the benefits are often overstated. In any case those tests don't have to be unit tests.
If I'm working on the kind of system that would need mocks for comprehensive unit testing, I'd rather either use integration or high level functional tests if that kind of strategy is viable. At least then I'm still testing that my actual production code works.
If that's not possible, for example because I'm integrating with an external system where I can't test operations that would cause real side effects, I'd rather look for a different strategy entirely. For example, I might change the design to better isolate the bits of code I can control and test myself, I might (re)implement riskier areas in languages with powerful systems that will guarantee various classes of programmer error can't happen, or I might not have an automated test suite covering some code at all and rely on things like code review or automated code analysis tools instead.
If you need to modify the tests together in a parallel fashion to the modifications you make in the code you are more likely to introduce same conceptual bugs in both places.
I have found one situation where these types of tests are useful to me. I'm working on a system full of legacy code and nearly devoid of tests. Most of the time a simple refactoring will make whatever feature or defect I'm working on much easier, but I'm loath to refactor without tests in place. The current design of the code doesn't allow for easy testing, so I'll write some tests using mocks to verify behavior. Then I can refactor with a little more peace of mind.
I actually liked mockito, when I was doing Java development a couple of years ago.
That just looks like a horrible test, I tend to argue that when you have to resort to using a verify on any mock then you are looking at something problematic.
The same argument could be made if you replaced the call to verify(...) with a when(...). The point is that mocking frameworks tightly couple the test code to the current implementation. That has downsides.
You are confusing testing with unit testing ( an unfortunate name) . Unit testing is a design tool not a testing tool. It has more in common with UML than with selenium.
You can in theory throw away your unit tests after you've implemented a piece of code.
It should be used for exposing weak design ( tight coupling for example) not for exposing nullpointer exceptions.
On the contrary. Unit testing is exactly for exposing that sort of thing. (Actually, I see more problems with cute off-by-one bugs than null pointers per se, but still.) They should not be thrown away.
There are ways to do it and ways not to do it, though. A good framework should be exhaustively unit tested. Individual components that use that framework should be lightly unit-tested (rather than having the framework heavily mocked) and more attention should be spent on integration/acceptance tests.
That sounds daft. One of the main benefits I can see with unit testing is that if you re-factor components below, then you can be reasonably sure it will work the same way afterwards. Why would you throw that away?
He may be confusing TDD with unit test, but he's right that unit tests tend to expose design rather than bugs. Unit tests are typically tightly coupled with the underlying implementation, and so unforeseen cases in the code (that will cause a bug) tend to be unforeseen cases in the test too.
Finding bugs is not the primary purpose of unit tests.
No, you are confusing unit testing with TDD. Unit testing is just testing units of code. TDD is the religious practice adhered to by some followers of Agile that is a "design tool" where you write tests first. http://en.wikipedia.org/wiki/Test-driven_development
>not for exposing nullpointer exceptions
If you are using a language primitive enough to have null pointer exceptions, then using unit tests to try to detect some of them is a perfectly reasonable thing.
Prior to mockito, unit testing was (more or less) limited to testing that your methods behaved as expected, and would occasionally expose NullPointerExceptions or other exceptional conditions. Dependent objects were either simplified or simply ignored. With the rise of mock object frameworks, however, your tests specifically say "this method on this mock will be called X number of times, with this result". Mind you, this is all happening in the context of another method call. So, for example, if you were testing the method "calculateDueDate", and that method took a DateTime object, your test might look like this:
The problem with this is that the tests become obstacles in the way of refactoring the code. Should you decide that you don't want to use the DateTime library any longer you will have to not just change the code which is using it but the tests as well. Or what if, going back to the example above, you decided not to use the getHour() method any more? Every test referencing that will have to be changed. And changing those tests is very likely to be more involved than changing the code under test, because there frequently are more tests than code. This has a negative impact on the design of the application. Because companies rarely dedicate resources to making existing software better purely for its own sake, you tend to have to do what you can when you can. This means your time is limited to make that refactor, or upgrade that library, or do whatevever change it is that needs to be done. Unit tests, especially those that use mocks, can get in the way of this to such an extent as to make such efforts impossible.I think testing is important. I do not, however, share the belief that is the sole, or even primary, determinant of code quality. In fact, an over reliance on unit testing can easily be a net negative. Should unit tests be thrown out? No. Baby with the bathwater and all that. But they should not be viewed as a silver bullet, either. They're not. They can help, but they can hurt.