You must be thinking: "Forget Why! What the h*** is DDAT?".
What is DDAT?
DDAT is an acronym for Developer driven automated test. The term is a mouthful, so I will use the acronym in the rest of the article. And maybe it will catch on. Wishful thinking!
DDATs represents tests, which I, as a developer, write to test my own code. These are tests that run automatically and are built by the developer for her/his own need.
I know the next question in your mind - "Why not just call it unit tests?". That is the name I also started with. But the term Unit testing is so prevalent, and it means different things for different people. So DDAT is almost like a Unit test, but more flexible to represent what I need from my tests as a developer. Having said that, if you substitute DDAT with Unit test, I am fine with it.
One of the reasons for not calling DDAT as a unit test is because it may not strictly fit into definitions of Unit tests, as widely held in the community. For example, many unit testing aficionados clearly recommend that unit tests should not be touching databases. There are very good reasons for it (primarily speed of execution) and I agree with those reasons. But what should I do when I am testing the data access layer:
- Should I create a unit test which mocks out the actual database from these tests? If I do that, then what am I really testing? What is the value of such a test?
- Should I just ignore tests for the layer? Can I live with that uncertainty?
Some people would say that we should just write an integration test for the data access layer. And hence, I moved to my own term - DDAT. DDATs are just good tests which allow developers to write good code in a confident manner. It allows developers to do refactoring with peace of mind. They might be unit tests or integration tests as per the need. But they serve their masters - the developers of the code.
This post is not about...
This post is not going to provide a recipe or tutorial for creating DDATs or Unit tests. There are numerous articles on the subject. Down the line, I will write about my own learnings on the topic. But this post is not going to cover it. This post is focused on Why DDATs?
I have already established that DDATs are a close cousin to Unit tests. There are a lot of detractors who site the disadvantages of Unit testing. So these qualms apply to DDATs too. Let us look at a few prominent ones:
- Extra work and extra time spent on tests.
- Have to keep them well oiled.
- These can give a false sense of security.
- These can be brittle and keep breaking.
All these are valid concerns, and we need to know them and manage them.
There are also patrons of Unit tests. What do they say?
- Unit tests are great at busting bugs
- They allow fearless refactoring of the code.
- They promote better collaboration between developers by acting as documentation.
- Good tests ensure that debugging issues can be much faster
- Improve the stability of the code.
These are true on the whole, but there are things to take care.
Now that, we are finished with thoughts shared by others, let me start with my own why's.
The cornerstone for creating a system of continuous improvement is Feedback. DDATs provide feedback during execution, but it doesn't stop there. While reading DDATs and the code & interfaces that these helped us to create, we get feedback on how the solution has been developed. We get feedback on our code design when we write the tests. The quality and readability of tests also tell us how good our code is.
To reiterate, feedback is how we create new and better things - ask any person who has built anything of significance. In development, instead of thinking up all the design ideas all the time, we can look at the code and its tests and these can reveal new responsibility owners & roles (as feedback).
Earlier we get feedback, faster we can improve a system. We can figure out mistakes faster. Debugging is faster with DDATs because it reduces the time you spend on debugging at the first place.
DDATs are a living feedback system. That is why!
Another aspect about writing DDATs is that we end up modularising our software. Whenever we have code that does a lot of things, it becomes very difficult to test them. So we split them up. For example, we take out stuff which deals with, say, external systems or databases into separate classes so that we can test the business logic without worrying about those concerns.
DDATs help us to develop a nose for overcrowded (non-cohesive) and coupled code. If we are mocking too many objects to test a System Under Test (SUT), it tells us that we have too many dependencies and the SUT is probably dealing with too many aspects of the system - time to split it up.
Why? Independent Development
A very important thing that DDATs enable me to do is, I can test things independently and move forward with development. I don't have to depend on other systems or other people.
As long as a test can run within the confines of my machine without having to reach a network, I find that test to be useful. It is ok to touch a file or db within your machine (this is my opinion and also why I call it a DDAT). If I am traveling and cannot connect to a network, I am still able to run my DDATs and hence make progress.
Having this ability gives me a lot of power and convenience. I can confidently move forward and don't need someone to endorse things. Also passing tests act as visible proof of things working instead of just a mental level confidence. Great programmers may not need this, but for a normal developer like me, this is a good thing. As an added bonus, failing tests give me a quick and focused way to debug my code.
Why? A client perspective
DDATs test our code by acting as a client to your code. This means that when we write them, we are not really thinking of the implementation, but about how the code is going to be used - the interface. It might not look like a big deal, but thinking in terms of interfaces - to think about what to do (instead of how to do) - is a key technique to creating quality software.
Seasoned developers do this interface based thinking automatically, but it is always good to have a tool in our box which helps us stay the course. For budding programmers this can be very handy. A more pronounced version of interface-based thinking is observed when you do Test First (TDD) - at least that is my experience. But even if TDD is not your cup of tea, DDATs written after can still be useful for achieving this.
Why? To conquer fear
A good battery of DDATs means that we have protection to refactor our code. This means that I can go ahead and improve the quality of my internals without the fear of breaking something.
There are too many times (can't keep count), when my code got better by 2x or more after refactoring. Without DDATs, I wouldn't dare touch working code. Just like we need insurance in other aspects of life, DDATs provide us with the same effect when writing code.
It's not all hunkydory!
DDATs might offer us a lot of good. But it does not mean that it comes for free. These are work - both in terms of quantity and quality.
When running against time (which happens in many startups and sometimes other companies too), creating DDATs is extra work. Also, they have to be done with our thinking caps on:
- We need to think through scenarios that need to be tested (especially the failure ones - we are particularly bad at it as developers).
- DDATs need to be written in a way that they are not too coupled with implementation details (which makes them brittle and render them useless).
- The tests need to be written with care so that, when they fail, the error messages make sense.
The mental effort is there. There are a bunch more things to keep in mind and that is probably a topic of another post. AND all this effort is not one time, it is something you have keep doing (keeping it well oiled). So I agree with what others said earlier.
One thing though - this effort is an investment for getting those benefits mentioned above. Also, if we do this regularly, we will build the mental muscle and the sensitive nose required for becoming better developers!
Another aspect that comes into play, is the language or platform or ecosystem that you are working in. Some platforms/ecosystems have a very rich set of tools and libraries which allow you to create DDATs with low effort. Others can make it a very laborious and tedious process.
So when you make choices of tech platforms, testability should be very important criteria in their selection. Neal Ford from Thoughtworks categorically mentioned this in one of his talks that I attended long time back. If you are stuck with a primitive platform (in terms of testing capabilities), you might be tempted to skip DDATs. I can't blame you, but in my opinion it is worth sucking it up and doing the work. It will add value.
DDATs might touch files and call other systems like databases. To me this is useful and manageable in the context of local testing in my machine. Using the same in a Continuous Integration (CI) setup can be a little more challenging.
Having said that, with technologies like containers (e.g. Docker) this is no longer a deal-breaker. I think with a bit of work, we can get things working in our CI setup as well and that makes DDATs super useful for the entire team. I have done this myself many times and so can you.
DDATs are not a silver bullet. They are useful to get the benefits like feedback and independent development, but that does not mean, you completely rely on them and forget everything else. For example, if we use DDATs as the only source of feedback and don't have a QA team doing exploratory testing then we are shooting ourselves in the foot. If we become brash about our independence and don't get the code independently tested and reviewed then God save us and our code.
A word of general caution: No form of testing (not just DDATs) will eliminate all bugs. Expecting that is asking for perfection which is an impossible task and a fool's errand.
If you have read this article you probably already know what I am going to say next. For others, the gist is:
Use DDATs since it provide critical benefits - feedback, independence, modularisation etc. But using them doesn't come cheap. And I would always use these.
Aum sarvam SriKrishnarpanam astu.