185

We have tried to introduce developer automated testing several times at my company. Our QA team uses Selenium to automate UI tests, but I always wanted to introduce unit tests and integration tests. In the past, each time we tried it, everyone got excited for the first month or two. Then, several months in, people simply stop doing it.

A few observations and questions:

  1. Does automated testing actually work? Most of of my colleagues who used to work at other companies have tried and failed to implement an automated testing strategy. I still haven't seen a real-life software company that actually uses it and doesn't just talk about it. So many developers see automated testing as something that is great in theory but doesn't work in reality. Our business team would love developers to do it even at a cost of 30% extra time (at least they say so). But developers are skeptics.

  2. No one really knows how to properly do automated testing. Yes we have all read the unit testing examples on the internet, but using them for a big project is something else altogether. The main culprit is mocking/stubbing the database or anything else that is non-trivial. You end up spending more time mocking than writing actual tests. Then when it starts taking longer to write tests than code, that's when you give up.

  3. Are there any good examples of unit tests/system integration tests used in a complex data centric web applications? Any open source projects? Our application is data centric but also has plenty of domain logic. I tried the repository approach at some point and found it pretty good for unit testing, but it came at the price of being able to optimize data access easily and it added another layer of complexity.

We have a big project undertaken by 20 experienced developers. This would seem to be an ideal environment to introduce unit testing/integration testing.

Why doesn't it work for us? How did you make it work at your company?

23
  • 14
    What's your technology stack? Feb 4, 2013 at 12:37
  • 7
    WebForms are nearly impossible to unit test properly. You can use an MVP (Model/View/Presenter) pattern to move presentation logic to a testable component.
    – Pete
    Feb 4, 2013 at 16:09
  • 12
    @MasonWheeler: In both cases, you've constructed a terrific argument that disproves premises that weren't accepted in the first place: that is, that unit tests exist to prove correctness. Feb 4, 2013 at 20:23
  • 10
    @MasonWheeler- Using that argument you shouldn't attempt any QA ever, because you will never prove there are no bugs. That's not even the goal. A good automated UI and unit testing strategy is just to free up QA from rote testing and allow them to concentrate on exploratory testing.
    – Alex
    Feb 4, 2013 at 21:14
  • 16
    I'm shocked that several people stated, they had never seen automated testing for more than a few months. I worked for about five big companies in germany as a consultant and they would fire you if you don't write tests. Automated Testing isn't a theoretical subject, it is practiced successfully around the world and it significantly increases code quality (if done right).
    – user80577
    Feb 5, 2013 at 15:12

16 Answers 16

91

The hardest part of doing unit testing is getting the discipline to write tests first / early. Most developers are used to just diving into code. It also slows down the development process early on as you are trying to figure out how to write a test for the code. However, as you get better at testing, this speeds up. And because of the writing tests, the initial quality of the code starts off higher.

When starting out, try just to write tests. Don't worry so much about mocking/stubbing things in the beginning. Keep the tests simple. Tests are code and can/should be refactored. Though along those lines if something is hard to test, it could also be the design. TDD does drive towards using most design patterns (in my experience, particularly the Factory pattern).

Make sure that the tests get a level of visibility. Integrate them in the release process, during code review ask about them. Any bugs found should get a test. These things are where TDD shines.

Here are a couple of resources that I have found useful:

http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf

http://www.agitar.com/downloads/TheWayOfTestivus.pdf

Edit:

One thing to keep in mind when you are writing tests. You are not trying to specify anything about the implementation of the code, only the behavior. When you write code, you test it all the time. Trying to execute it with debug statements and so on. Writing tests formalizes this and provides a record of the tests that you have. That way you can check your functionality confidently without accidentally skipping a test case that you remembered halfway through the development process.

4
  • Another way to approach introducing this as a diagnostic feature... aka power on self test (POST) that can be nearly shipping customer code... and not merely a bunch of simple tests, which is what tests and functions should be.
    – JustinC
    Feb 4, 2013 at 16:39
  • Also, avoid TDD Anti-Patterns.
    – Gary
    Feb 5, 2013 at 17:16
  • 4
    Misko Hevery also has some great videos on youtube about writing testable code which I found to be invaluable. youtube.com/watch?v=acjvKJiOvXw
    – Despertar
    Jul 6, 2013 at 1:24
  • "Make sure that the tests get a level of visibility" - this is critical for success. If no one can see how your tests are performing they won't see the value.The tests should be run on check in automatically as part of a continuous integration and then reported. I work at Tesults (tesults.com) and the reason it exists is because of the huge impact test visibility provides.
    – ajeetdl
    Jan 13, 2017 at 10:37
80

In many ways I agree with your team.

  1. Most unit tests are questionable in value. Since the vast majority of tests seem to be too simple.

  2. It is much harder to write good testable code than just working code. There's a large percentage of the developer community that believes in just get it to work, versus code/design quality in itself. And an even larger percentage who don't even know what quality code is.

  3. It can take much longer to write the unit test code than the actual code itself.

  4. Figuring out how to adequately test the more complicated code (ie. the stuff you really are interested in thoroughly testing) is beyond many developers capabilities.

  5. Maintaining unit tests takes too much time. Small changes can have big ripple effects. The main goal of automated unit tests is to find out if changes broke the code. However, 99% of the time what ends up breaking are the tests and not the code.

With all the above problems, there still isn't a better way to be able to make code changes and have some level of confidence that something didn't unexpectantly break than automating your tests.

Some of the above can be alleviated to some degree by not going by the textbook of unit testing.

Many types of designs/applications are better tested by automating tests at the module/package level. In my experience, most coding errors are not because the code in a class was coded incorrectly but because the coder didn't understand how their class was supposed to work with other classes. I have seen a lot of bang for the buck in this type of testing. But once again, these tests are harder to write than unit (class level) tests.

It really boils down to whether the developers believe in the process or not. If they do, then they'll write good unit tests, find errors early and be proponents. If they don't, then their unit tests will be by and large useless and won't find any errors and their theory of unit tests being useless will be proven true (in their minds).

Bottom line is that I've never seen the full blown automated unit testing approach work for more than a couple months myself, but the idea of automated unit tests still persists although we are selective in what really needs testing. This approach tends to have far less critics and is more accepted by all the developers rather than just a few.

13
  • 24
    I tend to agree with this.. We've gotten into the habit of making tests only after something breaks (even if that breaking was during development). Never up-front, takes too much time for too little reward.
    – Izkata
    Feb 4, 2013 at 15:57
  • 5
    @Izkata the other approach I've seen done successfully is to write a relatively small number of high level tests calling the top level Frobinate() method (instead of the dozens of smaller methods called below it) after the system has been verified by other means to serve as a smoke test that none of the lower level changes broke anything. Generally these tests used the same data that was part of pound on the keyboard user tests provided so the customer can see that the system is doing what they want. Afterwards code coverage tools can ID where edge cases aren't being covered yet. Feb 4, 2013 at 16:16
  • 3
    I didn't say "full blown automated testing", I said "full blown automated UNIT testing". Big difference. I've used automated testing at the module level for a decade at least. Unit testing is at the class level. I believe that I get more bang for the buck when testing classes that are supposed to work with eachother together, rather than as individuals. However, even there, we still use a pragmatic approach and selectively choose what/where to write automated tests.
    – Dunk
    Feb 5, 2013 at 23:34
  • 2
    Without good unit test coverage, how do you refactor? Or without refactoring, how do you prevent the code from gradually degenerating to unmaintainability? Mar 1, 2013 at 22:16
  • 1
    @Leonardo They didn't - they were too scared to change anything. Or they saved up all that technical debt and put aside a few weeks/months later on to address it in one lump.
    – GraemeF
    Mar 3, 2013 at 9:59
33

The main culprit is mocking/stubbing the Database or anything not simple.

And there is your problem.

Everyone makes good points about how to integrate unit testing into your environment. How to force people to do it enough that they see the practical value and it "sticks". But if it's super painful to do, and/or provides no benefit, it won't stick.

Stubbing out a database should be dead simple. Instead of your interface going to some DB backing to provide its results, you put in a simple hardcoded object. If you can't do that, then your design/architecture has problems. Your code assumes it is going to a database, or you don't have the interface abstraction to vary it.

This isn't simply a testing/quality concern. As soon as you want to change DB providers, or go to the cloud instead, or support unconnected mobile apps your design simply fails. If you cannot support the simplest of flexibility cases, you certainly cannot support the more complex things that your business will inevitably require.

8
  • 4
    Hard-coding database return values from a little stub of a mock object is a good way to insulate the test from anything in the database which could change and break the code (e.g. column renames). It's appropriate under certain circumstances, but an easy-to-use temporary testing database is important to keep around unless you want things to break someday when you change it. If your code breaks when you swap out the database, that's a fault of the code that the test should be catching (and if you want to avoid that, you'll want to run the test suite under multiple databases.)
    – user2348
    Feb 4, 2013 at 19:39
  • 8
    @fennec - unit tests aren't there to test the database, they're there to test the code that depends on database values to function.
    – Telastyn
    Feb 4, 2013 at 19:43
  • 4
    All well and good until you're unit-testing the code that manipulates the database. :P which, for a lot of people, is a lot of their code.
    – user2348
    Feb 4, 2013 at 19:59
  • 4
    @fennec - It's slightly more complex than dead simple to stub out the interface to make sure your writes are writing the right object. It only gets hard when your classes are trying to send SQL directly down your interface (read: you have a horrible design).
    – Telastyn
    Feb 4, 2013 at 22:13
  • 5
    @Telastyn perhaps I'm misunderstanding but eventually some class needs to get down and dirty and write the SQL or write the file or send the data or interface with the GPU. Most abstractions have unavoidable leaks at some level; they are simply pragmatic and not necessarily horrible. Feb 6, 2013 at 22:18
22

You need to start with something small, simple to automate, and high value. Pull down some sweet, low hanging fruit, and you will be able to sell the process. Show how it saved someone a late night or a weekend call. Then you can expand out from there.

To do automated testing well, you need someone who is a resource and an evangelist, and who has buy in from higher level managements.

Treat your automated test development like any other agile project. Produce completed tests on a regular basis.

Adding from comment: That is more of a management issue. Is the code considered "done" before it is documented? Before it is checked in? Before it includes and passes unit tests?

How you approach this really depends on your role. Are you a peer? If so, show others how it makes your code easier for them to reuse and maintain. Are you a lead? Pick your programmer who has the most code problems and help them add tests to avoid those problems. Are you a boss? Set it as a standard that "the code isn't done until the unit tests are in and passing.

3
  • 17
    "Pull down some sweet, low hanging fruit, and you will be able to sell the process.": I think they got to this stage already (they saw potential advantages in using unit tests) and this is why they were convinced to give it a try. The problem is rather how to scale the low hanging fruits to doing unit testing systematically. The only project I worked on in which unit tests were used systematically had more unit test code than actual product code. If a team is not prepared to spend more time coding unit tests than actual application code, then IMO the approach will probably not work.
    – Giorgio
    Feb 4, 2013 at 13:01
  • 4
    That is more of a management issue. Is the code considered "done" before it is documented? Before it is checked in? Before it includes and passes unit tests? How you approach this really depends on your role. Are you a peer? If so, show others how it makes your code easier for them to reuse and maintain. Are you a lead? Pick your programmer who has the most code problems and help them add tests to avoid those problems. Are you a boss? Set it as a standard that "the code isn't done until the unit tests are in and passing. Feb 4, 2013 at 14:00
  • 1
    @SkipHuffman your comment should be added as an edit to current answer. Feb 4, 2013 at 14:02
15

Follow these ground rules. Tests:

  1. must run regularly! You can make tests run on every build, before/after every checkin, or just every morning. Automatically triggered is highly preferable to manually triggered. Because while in theory you can have everyone on the team be responsible for ensuring they run tests, if it's not automated, it probably isn't happening often enough! And if you don't run your tests often enough, they are both finding the bugs too late encouraging lots of broken tests, which leads on to point 2:

  2. You are still only going to succeed if those tests, now running regularly, don't get in the way. By which we mean tests:

    a. mustn't take too long to run (subjectively) for the value they provide! Make your tests blazing fast. Don't let people check in tests which are going to be a waste of your time to let them run!

    b. mustn't be unreliable. Avoid multithreaded tests if at all possible. Apply engineering practices to your tests just like your other code: particularly - code review your tests!

    c. mustn't be harder to fix and maintain than the actual code tested. Your coding velocity is really going to suck if a tiny one line change to your codebase requires you to fix 10 different tests.

And finally, rule number 3. Tests must not only fail to provide negative value, as in rule 2, they must provide positive value. Tests...

  1. must actually be telling you something you care about when they fail! (No tests with obscure error messages, or merely giving you ridiculous complaints like 'you forgot to run the test on a Windows 2008 machine', please!).

One popular way to violate rule #3 is to test the wrong thing. This is sometimes because a test is too large or too unfocused. But usually it comes from not testing something a customer will care about, and testing irrelevant implementation details. (But sometimes testing implementation details makes an efficient test too - IMO it just takes practice deciding which.)

Conclusion: these ground rules point you in the general direction of a sustainable testing discipline, which is what you are desperately craving. When testing, ask yourself if this test is really sustainable and maintainable. Remember:

  • if tests aren't sustainable, they fall into disuse, and thereby become wasted effort
  • if tests aren't sustainable, you stop doing tests, and your team stops getting better at testing! And, final point:

Testing is actually hard. You should expect that your team's tests will basically suck when you start writing tests. Don't get discouraged. Do throw away old tests, whenever you notice they suck and are unsustainable.

12

1. Does it really work?

Yes, it does - if done properly. The point is that testers needs to adjust and extend their automated scripts after engineers implement new features.

2. No one is really experienced or knows how to properly do automated testing.

Get a consultant (someone who knows how it is done properly). Or, invest more time. The alternative is to have bigger testing team, who do the same testing manually (which is error prone).

3. We have a big project with 20 good experienced developers working on it. So it should be a great environment to introduce unit testing/integration testing. Why doesn't it work for us? How did you make it work in your company?

I wouldn't call them "good experienced developers", if they refuse to do unit tests. There are many great articles of the positive benefits of testing (both unit and integration testing), and at the end it boils down to how much a bug costs your company. For example, I work in a company where a quality matters, therefore unit and integration tests are unavoidable. You can easily find lots of articles which tells that unit tests only are reducing number of bugs by 30%! (Actually, it is in the 20-90% range, on average 30%, but it is still a lot.)

To make it work in your company, either hire a consultant, or assign this task to a senior engineer (it will take him a while to do it). And then, force everybody to stick to the rules.

7
  • 22
    I have to point out that practically EVERYTHING works "If done properly". However, that is not very helpful to all but a very tiny minority of any population. For a process to really be able to claim that it works, it must also work when done "sort of".
    – Dunk
    Feb 4, 2013 at 15:32
  • 12
    I will point out that over-generalizing everyone's situation to your own (ie I wouldn't call them "good experienced developers"..quality matters) not only demonstrates your lack of breadth in experience but isn't very productive. Every industry has their own definition of "works"; and what degree testing needs to be done at the unit, integration and system levels. Many "exceptionally good" developers have come to the realization that unit tests provide little bang for the buck when compared to automated integration testing. They also realize this likely only applies to their specific industry.
    – Dunk
    Feb 4, 2013 at 15:43
  • 12
    "I wouldn't call them 'good experienced developers', if they refuse to do unit tests." This is just the No True Scotsman fallacy. The software industry got along for decades without unit testing and most of that industry continues to get along without it today. It may be a good idea but it is simply not mandatory. Feb 4, 2013 at 17:28
  • 1
    "not waste time on unit tests": I would rephrase this as "not waste time on useless unit tests". Blindly unit testing everything can result into a huge waste of time.
    – Giorgio
    Feb 7, 2013 at 22:39
  • 1
    @Dunk I really hate the whole test-first-everything TDD phenomenon but I can't agree with your first statement. You're either doing a thing right or you're not. You can do one unit test well and maybe see the merits of it but you'll never see the merits of any one thing done half-ass. Jul 6, 2013 at 4:14
11

One thing that I haven't seen clearly addressed in the answers above is that unit testing is essentially a public good and a private cost. I've written a blog post about it here.

What it comes down to is that while a suite of tests benefits the team or an individual developer, writing the test is a cost to the one doing it, most of the time.

In short, unless writing the test is enforced somehow -- and the answers above list a number of different ways to do that -- there's no reason for an individual developer to do this.

In one company that I've worked at, writing unit tests was a required part of delivering a feature. New code was not accepted unless a unit test was part of the commit or new feature -- there were brief code reviews for every "task" that a developer was given. It may be worthwhile to implement a similar policy at your workplace.

10

They are many reasons why introducing automated testing might fail. I think that it boils down to the fact that programmers tend not to change their coding habits and are not fully capable of embracing unit testing.

Many people who want to start with automated testing try to introduce them for an existing code base. They will try to write integration tests which test a lot of functionality of existing application at once. Such integration tests are notoriously too hard and too expensive to maintain. Advice: Introduce automated tests for a new code base.

Unit tests are good tests to be automated. Everything above (integration tests, component tests, system tests) can be also tested automatically, but the cost-benefit ratio rapidly declines the more functionality is tested at once. This negative effect is amplified, if you build such tests on poorly unit-tested functionality. Advice: Introduce automated tests on the unit test level and build automated integration tests on a solid foundation of unit tests.

From the points above much of the success of automated testing depend on how effective the unit tests are. You have effective unit tests if you feel productive with unit tests. When people start with unit testing, they tend to retrofit their existing code and coding habits into unit tests. Ironically this is the hardest way to learn unit testing. Also unit testing requires you change the way how you code (for instance applying the SOLID principles ). Most programmers soon stop writing unit tests because they think that the learing curve is too steep and find it awkward to wrap unit tests around a not so testable designed code. Advice: Learn unit testing from the ground up with new code and deal with the fact that you need to change your coding habits.

There are a lot other factors, but I found that for most programmers it is troublesome to change their way to code. Code written without test just looks different. If you cannot squeeze your code into a testable design, you will very probably fail to write effective unit tests. This destroys the ground for effective automated testing.

I experienced that by myself and I am now happy to work in a company which successfully introduced automated tests. I could write a lot more about the other factors, but I think that coding habits and unit testing are the most important ones. Luckily there are others who have more experience than me and fill books with their know-how. One of these books is Brownfield Application Development in .NET which I can really recommend, since you are using the Microsoft technology stack.

1
  • Introduce automated tests on the unit test level and build automated integration tests on a solid foundation of unit tests. +1
    – c69
    Feb 4, 2013 at 22:39
8

It is interesting that the business is more pro-testing than developers! It sounds to me like your biggest challenge will be to overcome your developers resistance to change; they need to re-define their understanding of their jobs to include unit testing.

Nothing can help you more than early unit-testing successes to help your developers overcome their resistance to writing these tests. If you push them to do something new, make sure you push first for something with a nearly guaranteed reward.

@SkipHuffman touched on this, but I'm going to say it outright. Some things are much more suited to automated testing than others. For the first pass, I would NOT test the database or the UI. Input from a database can be extremely difficult to set up and tear down. UI output tests tend to be quickly broken by look and feel changes that are completely irrelevant to your tests.

What I'd call "middleware" is perfect for unit testing. Code that has clearly defined input and output conditions. If you follow the DRY principle (Don't Repeat Yourself), you will have written some little classes or functions to solve recurring problems that are unique to your application.

Unit testing is a great tool to limit the risk of changing existing internal components. Write unit tests before changing an internal component that has worked for a long time. These tests prove that the currently working functionality is preserved. When you have made your change and all unit tests pass, you know you haven't broken anything "downstream." If you do find a downstream issue, add a unit test for it!

Ron Heifitz would say to "address conflicts in the values people hold, or to diminish the gap between the values people stand for and the reality they face. Adaptive work requires a change in values, beliefs, or behavior." After you overcome the human resistance to change, you can branch out into more difficult testing areas as appropriate.

1
  • 6
    "overcome your developers resistance to change": not every resistance is without a reason and one should not avoid an honest discussion by using the "resistance to change" argument.
    – Giorgio
    Feb 7, 2013 at 22:41
7

One thing about automated testing is that it requires you to write code to be testable. This is not a bad thing in and of itself (in fact it's good because it discourages a lot of practices that as a rule should be avoided), but if you're trying to apply unit testing to existing code then the chances are it's not been written in a testable way.

Things like singletons, static methods, registries, service locators and so on introduce dependencies that are very difficult to mock out. Violations of the Law of Demeter mean that too many parts of your code base know too much about how other parts of your codebase function, introducing further hidden dependencies that can be difficult to break. All these things make it difficult to isolate a module from the rest of the code base, and if you can't test your modules in isolation then unit tests lose a lot of their value. If a test fails is it because of a fault in the unit under test, or because of a fault in one of its dependencies, or perhaps it's because the data that's being pulled in through a dependant data source isn't what the test writer expected? If you can't replace dependencies with mocks then any of those are a possibility.

Most codebases I've seen that weren't built with unit testing in mind tend to be inherently untestable, as coders tend to focus on making the code work as they expect it should rather than doing the work necessary to keep coupling loose and dependencies explicit. Code that was written with unit testing in mind tends to look very different.

A lot of people take a naive approach to unit testing when they start doing it for the first time, they think they can just write a load of tests for an existing codebase and all will be good, but it never works out that way because of the above mentioned issues. They start discovering they have to inordinate amounts of setup in unit tests to get them to run at all, and the results are often questionable because the lack of isolation in the code means you can't track down what caused a test failure. They also tend to start off trying to write "clever" tests that demonstrate some highly abstract aspect of how the system should work. This tends to fail because a "clever" unit test is a potential source of bugs in itself. Did the test fail because of a bug in the tested module, or because of a bug in the test? A test should be so excruciating simple that there's obviously no possibility that a bug could be hiding in it. In fact the best tests are rarely more than 2 lines long, the first line instructing the unit under test to do something, the second asserting that what it did was what was expected.

If your team is serious about adopting unit testing then it would be unwise to start with an existing project. Your team's existing projects are probably untestable without major refactoring. You're better off using a new project as the basis of learning about unit testing, as you have a clean slate to work with. You can design the new code base to favour dependency injection over singletons, registries and other such hidden dependencies, you can write it to depend on interfaces instead of implementations and so on. You can also (and should) write the tests along side the code being tested, as writing the tests afterwards results in unit tests that make sure the tested module does what you think it might be meant to do rather than ones that test that it does what the specs say it should do.

Once you've gained some confidence with unit testing, your team will probably begin to realise the flaws in their existing code that are going to be obstacles to unit tests. This is when you can start working to refactor existing code to make it more testable. Don't be ambitious and attempt to do this all at once, or attempt to replace a system that works with an entirely new one, simply start by finding the bits of the codebase that can easily be tested (the ones that don't have any dependencies or where the dependencies are obvious) and write tests for those. I know I said writing a test alongside code is preferable to writing tests after, but even a test written later still has value as a starting point. Write the tests as if you know nothing about how the class works other than what its specifications say it should do. When you run the tests and get failures, then either the specs or the implementation is wrong. Double check both to determine which is wrong and update either the test or the code accordingly.

Once you've picked off the low-hanging fruit, your real work begins. You need to start finding the hidden dependencies in your codebase and correcting them, one at a time. Don't get over-ambitious at this point, just stick to doing one module at a time, or even just one single issue in one module, until the obstacles to testing are fixed and you can move on to the next bit.

TL:DR: Most people think testing is easy and you can retrofit tests into existing code easily. Both of these assumptions are wrong. If you embark on a project to get unit testing into your projects with both these facts in mind you're more likely to succeed.

1
  • hint: put the TL;DR: at the top - I had to read all your post just to get to it! (which kinda defeats the point)
    – gbjbaanb
    Sep 1, 2014 at 9:46
4
  • Is there anyone at your company with extensive experience doing automated testing?

If not, automated testing initiatives will probably fail. Automated testing is a skill, like many other skills in programming, and if you don't have anyone with experience doing it, it's not easy to tell whether an automated test is a good automated test with real value, or a bad one which will fail randomly / require frequent updates / doesn't actually exercise any interesting code.

  • Does that someone have leadership power? Are they able to demand change?

If no one listens, it doesn't matter if they say the test is no good. (Note that the leadership power doesn't have to be formalized. Having a team that cares is good too.)

  • Are you developing tools and processes to make automated testing easier to implement and integrate into the development cycle?

Developers are lazy. You need to make the things you want them to do easy to accomplish, and the things you don't want them to do more difficult to accomplish. You should make sure the testing libraries make it easy to do the tasks associated with test setup and teardown, especially environment-related setup, like test databases or the like. (Mocking the database is discussed in some of these comments but should be used with caution. A real database should be easy to spin up, and lets you test the interaction of components and process lifecycles, often more important and more effective than unit-testing an individual data-accessor.)

You should also make sure that your IDEs have a good way to launch the test suite. You should run the test suite often so that people notice when it fails rather than letting it linger in misery. Developers also respond well to feedback, e.g. an automated integration system reverting their changes if they've broken a test. Or, better, positive feedback: an automated integration system which catches bugs, and saves you from breaking things.

1
  • I don't think it's fair to say developers are lazy. Maybe that's the case in your experience, but surely it's not a universal truth.
    – Sam
    Jun 27, 2013 at 6:15
4

First, if your developers don't see the value of your tests, then it's probably because your tests aren't valuable, not because your developers are blind to their value, or to the value of tests in general. Among its evangelists, there is a tendency to believe that test driven development cannot fail, it can merely be failed by lazy, lazy developers. I think this is wrong and counterproductive.

When I was introduced to test driven development, it meant, effectively, writing a test to verify that a method that will never fail never fails. Which is nice, at first, because you get a lovely green check and a sense of accomplishment. Later, after you refactor the code, you have tens of infuriating red Xes, none of which say anything more than that the code has changed, that the tests are no longer valid, and that you wasted a lot of time writing them.

You want to avoid that.

Since then, I've taken a different approach to tests. Instead of an interface implementation pair, I have an interface, implementation, test triple. The interface specifies the behavior, the implementation performs the behavior, the test checks the behavior.

I suppose it seems obvious, but to me, it distinguishes between the code you must prove works as specified and the code you may test as much or as little as you consider appropriate. The code you must prove is the interface you offer to the outside; the rest is your concern alone.

In this case, I would ask the developers whether they see a natural division in the code where this sort of test would be appropriate. Is there an interface that Team A implements and Team B uses? In that case, it is in the interest of Team B to ensure that the interface behaves as they expect. Ask Team B to write a test for it, then tell Team A to ensure that their implementation conforms to the test; or, if it doesn't, and it intentionally doesn't, to discuss the unexpected change with the other team, so that they can prepare for it.

I think this would illustrate the value of the test. It isn't an end in itself, lovely green checks notwithstanding. It exists to make explicit the promise made by one developer to another, and to ensure that the promise is kept to the satisfaction of both.

2
  • 1
    I like code that I can read better than code that somebody felt needed to be tested right down to the minutiae like that. Jul 6, 2013 at 4:20
  • 1
    The lovely green ticks I feel are the problem - it makes testing into some sort of game.
    – gbjbaanb
    Sep 1, 2014 at 9:48
2

Adding lots of unit tests to a large pre-existing project is hard work. If you've already found a good mocking framework that works for you then you should have solved the hardest problem.

I suggest trying to add tests as you add features/fix bugs. Fixing bugs being the easiest one. Write a test that fails due to your bug and then fix the bug. At the same time you'll probably find yourself writing some simpler tests that do pass. Of course you really want to use a small and easily tested bit of code for this.

Once people start getting used to writing tests for the easier things you should hopefully find they write their code to be more testable.

I would also recommend that you measure the code coverage of your tests (I've used cobertura for Java in the past). You'll want to have some continous integration server running the tests and producing the metrics on a regular basis (every day, every check-in). If your fellow developers are keen then they will want to see the coverage increase over time and they can see the gaping coverage holes in some of your

2

I think you may have to play the long game. One thing you can do to get some acceptance is to attempt to exhaustively unit test the next feature you write and then keep track of the number of bugs over time. You should hopefully find that the major bugs will be caught early on (particularly if you couple this with Test-Driven Design) and the number of regressions should be very low. After a period of time, say 1 year, compare the stats with non-unit tested features of similar complexity. If you can show that the number of new bugs and regressions is appreciably lower then you have provided a financial justification for it and it becomes harder for the product team to ignore.

I had a situation where I was able to use TDD and unit testing for a major feature. After the end of the development phase there wasn't a single bug reported in over 5 years. When a new -- and risky -- enhancement was requested, I was able to implement it and catch all the regressions in the unit tests.

1

It is my strong opinion that unit tests' value is largely underestimated by many teams because of several factors, many already highlighted in the answers.

Often developers are under pressure to "get things done", so proving that a code block works is a sufficient proof for the customer. This almost always applies to consulting company and human-driven QA: if the customer doesn't require unit testing and coniders a live demo sufficient then the customer has totally failed since he's going to sign approval for code that might hide faults.

Often developers are frustrated. Being a programmer is a hard job: finishing a task and going to the next is satisfying, so everyone wants to hurry and finish. Until they get hit by a bus with a major bug that rises months after original QA. In this scenario, automated and continuous QA is a management's problem rather than developers (they'll still get paid for their work, perhaps overtime).

But there is an exception

I strongly believe that the acceptance of the automated test model is a function of the "humanness" of the tests being done. If you are testing a web module with a front end, you are more likely, despite tools such as Selenium, to fill the form by yourself, see the result and believe in determinism. You'll forget to re-run tests later or you'll just be too lazy to do old tests again, and this is why bugs sometimes get discovered later. To leverage this, a strong modularization of the code and strict rules on "modifying old code" have been proved acceptable in a banking environment (in my personal work experience).

Instead, if the developer is in charge of developing a highly-automated and high-volume-data module, he'll be more likely to write thorough unit tests and submit them to the test batches. This because filling a large XML payload with data converted from an external data source (mocked or not) is not a human-prone job. Some test developers will eventually build a tiny and funny front end for this specific kind of tests. When I worked at my Master's thesis I was working on a logging bus that handled 6000+ syslog messages per second and I had to measure packet loss and corruption: I naturally wrote unit and stress tests for almost all components, especially the Syslog parser.

In order to make developers more unit test-prone

I believe they must be forced to. If you are a smart customer you'll require your consultants to run the full test suite at every QA. If you are a good team leader you may think assigning the following task to a smart developer: build an inner-test platform. That has nothing to see with inner effect platform antipatter, but instead is a set of helper classes, database mocks, configurations, parsers, converters, swiss army knives to help developers build tests in no time.

Current testing platforms such as NUnit are general-purpose and allow you to verify generic assertions. Correctly using dependency injection and project-specific factories help developers write less code for tests and be happier. I haven't had the chance yet to experiment this on a full project, I can't provide real-life feedback.

1

Automated testing is like software development. Unfortunately the people you hire for testing are originally intended to write test cases, plans, strategy, follow review process, manually test and log bugs.

As soon as they are given automated testing responsibilities it includes some amount of software development. The catch here is, that automated testing, regardless of what tools you use (and for heaven sake dont argue this point), need maintenance and updating on a daily basis. As developers change code,

  • you need to ensure that tests are kept running.
  • You need to ensure that tests are not removed since they did not run
  • your test metrics needs to show what you ran on the last build and this build. To ensure that your # of test cases is not reducing.
  • test cases need to be reviewed just like development to ensure that people are not messing up, kinda breaking 1 test into 2 just to bump up the numbers (some times testing is outsourced, so this tracking is important)
  • a lot more "healthy" communication between dev and test is important
  • keep non-functional tests separate, and don't expect them to run it daily, it takes time to keep these uptodate, and good. But dont give up, ensure that they are maintained.

You fail due to these reasons

  • your test engineers are manual test engineers with no analytical skills. they dont know the difference between a if and a while loop. Because frankly no course teaches automated testing, they only teach manual testing.
  • your test engineers are too busy manual testing your builds, and logging bugs so they loose track of automated tests
  • your test managers dont care for metrics from automated tests, just because they show invalid data (when the project starts), and they dont put effort or priority in daily standups and meetings to stress how important it is to get automation up and running
  • you choose to automate tests for mobile applications, which have a very short shelf life. by the time you write, stabilize the automated test suite your application requirements change, instead you should focus on testing your webservices that run your application
  • you dont understand that automated test team follows the same milestone are development team, feature complete, code complete, code lockdown and code freeze.
  • you dont differentiate between manual testing folks and automated testing folks.
  • they both get paid the same salary and report to the same manager, ideally they should report to the dev manager and their salaries should match those of development.
  • you actually think and believe that junit is not enough for developing automated tests :).

These are from my experience working for companies who take automated testing very seriously and understand that dev is important as automated testing engineers. And from my experience working for people who dont know, understand the difference, no matter how much you explain to them.

2
  • In the case of unit- and integration-testing, the people who should write the tests are the developers, not separate "test engineers". (As written in the question, the QA, i.e. the test engineers, actually already use automated UI tests.) Feb 6, 2013 at 19:46
  • Realistically speaking, anyone writing automated tests should have development skills.
    – Siddharth
    Feb 7, 2013 at 3:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.