Testing is a lot about zooming in and zooming out. When zooming out leaves the product borders, it’s about more than just testing.
Zoomies actually describe the frenetic random activity periods that dogs and other pets have. They are random bursts of energy in which they run around frenetically.
Then there is an activity as part of Systems Thinking which is zooming in and zooming out. According to studies at the Cornell University people tend to not zoom out. Nearly everyone zooms in, nearly no one zooms out. I think that I’m part of “nearly no one”.
In Systems Thinking theory zooming in refers to breaking up a whole into parts. Then taking a part and breaking it up again, and so on. Zooming out is taking a whole and looking at it as part of something bigger. Then taking that again and look what it’s a part of. When using different perspectives zooming in and zooming out is not necessarily a repeatable process.
Example: add a new field to an existing web form.
Zooming in:
what type is that field on the UI, how large is it, where is it positioned
when sending the form, what type of field is the parameter in the e.g. JSON
when processing the field, how is it parsed and treated in code
when it ends up in the database, in which column does it go, what is the definition of the column
We are taking a new requirement (field), breaking it down into its parts (UI, API, DB). And then we are looking into each part and go into more and more details.
Zooming out:
what form does it belong to
what is done with the data after it’s being stored
which other elements of the product are impacted
what does that mean in terms of deploying
are there other dependencies when delivering the new feature
do we have mechanisms in place to deliver such a change in a safe way
And then there are the zoomies, constantly zooming in and out:
new field in the database -> do we need a migration script -> impact on other fields -> impact on other functions
migration script -> could there be runtime issues, what about the performance, do we need a downtime -> do we have everything in place for a downtime
delivering the new version -> migration script -> rollout, what happens in case of failure -> how to rollback the migration script
migration scripts -> do we have mechanisms in place to run migration scripts, do we have mechanisms in place to automatically roll back the migration
rollback of the migration -> what is the process looking like, whom to inform, what to document
rollback of the migration -> what impact has the partial rollback on other elements of the delivery, do they need to rollback as well
And so on. This is what partially goes on in my head within seconds. Call it ADHD, hyperfocus, bonkers, whatever you want. This is only a tiny fraction that goes on in my brain, when I hear about a new requirement.
Systems Thinking is about constantly improving, adjusting, correcting, extending, and much more of our mental model. The product and its elements are only one part of this. The ecosystem of the product, the process landscape, teams and roles, the company and the customers/users are also a part of the mental model. And I don’t stop at the product borders. When I see something is missing in the process landscape or not supporting good enough, I tend to try do something about it.
I know a lot of people who are very good at zooming in. I also know that a good portion of them is also zooming out. But too many stop at product boundaries. When it comes to evaluating risk and impact, zoom further out. Look at the bigger picture. Practice to zoom in and ZOOM OUT!!! Understand the bigger picture beyond the bigger picture.
I want to take the time to look back on 20 years of success and failure.This blog will be long, unstructured, ugly and boring. I write this post mostly for me. My advice is: Don’t read it!
T-Systems – the early years
I started in IT in March 2000, with joining debis Systemhaus, which was soon after bought by Telekom and merged into T-Systems. I was mostly doing Reporting, Data Warehouse and eBilling projects. End of June 2003, while my first Java project came to an end, I was interviewed for the role of an operational tester for one of our two big Test Factory projects. July 1st 2003 was officially my first day as a tester. On that day I could have already realized that software and testing is a lot about people and that testers are living with a special pressure. The ~50-60 testers of the project that we were back then had a facilitated de-escalation workshop that day; as I learned, the mood and temper in the team was horribly tense and aggressive. So I spent my first day witnessing how the team tried to deal with anger and disappointment. This event should mark the start into what has today become a 20 year long career as a tester.
The project I joined was the back then famous project “Toll Collect”, the introduction of a distance based charge for trucks using German’s Autobahn network. The project was already running for about 1.5 years and should go live September 1st 2003. The pressure was immense. Late in July or very early in August 2003 it was decided to cancel the project start due to a list of problems. It was already after a few weeks when I learned how important testing can be for the success of a project. When I joined we had big trouble keeping the system running under load for 13h, which was the initially planned auditor test scenario for the go-live. Remember that: 13h.
The start of the project was delayed until Jan 1st 2005. I had some time to get used to “my” subsystem, and one ability became apparent that I realized only about a decade later. Systems Thinking. Understanding things, patterns, behaviour, finding names, having a more structured approach only years later, is a common pattern still true to this day in my career and you will read similar things some more often. When combined with common sense, you know that thing that is not that common anymore, it’s an even more powerful tool.
Good Systems Thinking helped me to quickly understand
Good Systems Thinking helped me to quickly understand the whole Toll Collect system, consisting of 15-20 more or less complex subsystems. The system got more stable, problems were fixed, solutions were found. While we were preparing the project start during late summer and autumn of 2004, I got, in addition to my role as sub-team lead (of 3 testers and 2 sub-systems), the role of load test coordinator, supporting the designated test manager. That autumn we had three, each 2-weeks long (much more realistic than the original 13h load tests), and auditor accompanied load tests. Especially the third one I will always remember and tell people about it if they want to hear about it or not. While the first two tests were to show that the system was able to work under expected load for the standard and fallback scenarios, the third was to show how resilient the system was. An auditor was sitting with a super admin in a room, and from time to time told them to corrupt some system, database, network connection, you name it. The goal of the test was to see how quickly would a ticket pop up, is the failure visible in the monitoring, would the system failover properly, continue running on the other node, would operations people know what to do, how quickly would backlogs be processed, and so on. You might have already guessed it. What in 2011 became popular by Netflix under the name of Chaos Engineering, was performed here 7 years earlier as a standard test scenario in project Toll Collect. And yes, keeping the system under load for 14 x 23h (due to simulator restrictions), was not an issue at all. 13-14 months earlier we were not able to keep it alive for 13h.
In the next 3 years until end of 2007 I became deputy team lead of two test teams, learned all about several other subsystems, and even learned my basics of the SAP system. While project “Toll Collect” was the laughter of the nation end of 2003 and all year 2004, after the successful and pain-free start in 2005, the project was running quietly and smooth. Two major releases a year got properly tested, verified and rolled out without issues in production. In the meantime I made my ISEB Foundation certificate and my iSTQB Test Manager certificate, learning not much more about what I kind of did for several years on a daily basis. The whole project was heavily iSTQB driven, so I had no choice than to get indoctrinated from the very beginning of my career. It was so strict, that it was even not allowed to raise a bug against a subsystem, where no according test case was written in advance of the release start. Exploratory Testing was frowned upon, while it was actually nearly all that I did all year. I hated writing test cases upfront. How would I know several months in advance, what I’d discover while observing the system under load myself.
In 2006 I managed to “test” a new sub-system out of the system again within two releases. I didn’t like the idea of the new sub-system and found it was completely wrong and useless. In addition the initial dev team of “my” subsystem handed over “my” subsystem to the team that should implement also this new system. They had no idea about the intricate database design, didn’t listen to what I was trying to tell them, and produced this new piece of waste. Against the first release I raised 85 bugs and prevented it from going live with the planned release. That was more than a third of all bugs that 70+ testers raised against all 15-20 subsystems in the same time. Two releases later the system was history again. I was not able to prevent it from wasting a lot of time and money or even reaching production for the time of one release. But I’m still proud to say, that it was never used in production. It was the first time that I actively influenced the architecture of a system.
As a tester it’s often not your turn to bathe in the light of success.
Another anecdote about the lack of understanding of the new dev team followed in 2007. I was not responsible for that system anymore, when a production bug occurred. One record caused some issues. When the Test Factory was informed, the project lead came running to me, gave me the production database password and told me to invest an hour or two into the analysis. Two hours later I was able to tell them what happened, why it happened, and what to do to correct the record accordingly. It took two weeks for the dev team to analyze the record, come to a nearly correct conclusion about what happened, and to come to a wrong conclusion what to do about it. But they sold it as their victory, while including my project lead and me, in addition only two of the responsible people on internal customer knew that I solved the problem already two hours after it occurred. As a tester it’s often not your turn to bathe in the light of success.
T-System – Advancing into Test Management
In 2008 I advanced in my career and got nominated as the leading test manager for the international road charging project of T-Systems. Together with a bunch of my former colleagues of the German system, we tried to help develop a more light-weight system and sell it to some of the European neighbors that tried to introduce a similar system for their road charging initiatives as Germany. It was a good time. We tried a few light-weight approaches with an external dev team working in 2-week sprints. The rusty, slow oil tanker of our iSTQB heavy approach was not even closely able to keep pace with this. We tried to adjust, but the indoctrination of years of long-cycle waterfall approaches slowed us down. Nonetheless, it was a good year.
In 2009 the German project had some personnel problems, and I was brought back to the Toll Collect project as Release Test Manager. The job was preparing a release for 3-4 months, coordinating and reporting the release tests for about 4-5 months, accompany the rollout for a month, cleaning up the project for another 1-2 months. Then keep it a bit slower, support the next release a bit, and slowly start again with the whole shebang for the release after that. We were usually a team of four, always pairing up, one lead, one support and back-up.
In the next four years, I was the responsible Release Test Manager for the first and long time only release that did not make it to production in time. So I was also responsible from testing side for the only double-release rollout so far. That release was stressful, full of red reporting slides, troubleshooting phone calls and what not. And during the unplanned extension of the release my daughter was born, which didn’t make it any easier. Furthermore I trained a handful new release test managers, coordinated large portions of the first hardware exchange, I was the responsible release test manager for introducing road charging on the first 1200km of federal highways and I was the oracle of ancient process wisdom. Whenever someone wanted to know why we were doing something the way we did, I could tell them. I knew all the processes, and more important, I knew all short-cuts to speed things up. In the four years as test manager I hated our time-consuming test approach to the bones. Now I was in a role where I could shift things around, react to short-term customer requests, bypass teams when I didn’t see the value. Teams were not often happy about that. I did a lot of things my way. If that was good or not, I still don’t know. I was respected with both internal and external customers and colleagues, and people came to me when they wanted to get shit done. In 2012, my last year in the project, someone from the external customer called me, because they wanted to know something very specific about what used to be “my” subsystem in 2003-2006. I was renown in the whole project to be the person with the second-most knowledge about this system, behind the person on Toll Collect side who re-designed the sub-system from 2007-2012, who happened to be on vacation that week.
Nine and a half successful years as a tester came to an end. It was time for me to move on. In September of 2012 one of our team leads shared a newsletter from EuroStar conferences about a series of free webinars on testing topics. In over nine years, I got the ISEB Foundation 2004, the iSTQB Test Manager 2005, ITIL v2 Foundation in 2005, a seminar on risk-based test management in 2006, and participation in the course of one of the other advanced level iSTQB courses in 2008, as they were given in-house, where I never took the exam. And in 2012 I got a really cool seminar on project management with a lot of basics. Maybe a bit late already, but you know… better late than never. Anyway, I didn’t know about the testing community, testing conferences, webinars, books (except the one I got for the advanced level, co-authored by a colleague), blog posts, Twitter and what not.
I had to realize after this first webinar ever, that I didn’t do one thing in 9 years. I didn’t ask many questions, I didn’t try to understand basics, I tried to fake it until I make it. I saw asking (stupid) questions as weakness between all these seasoned testers, developers, project managers and leads. Oh, what a waste of time. I was for a long time one of the youngest people in the team, and one of very few that did not have a university degree (at one point we even had 13 out of 70 people with a Ph.D.). 9 years I accepted the processes defined by people who were no longer in the project, I accepted terms, and tried to learn how to use them in the right context, rather than understand what they actually meant. I knew a lot of things, but it took me forever to learn them, I rarely questioned things, because someone more intelligent than me has introduced them for a good reason, I thought. In that week started my awakening as a tester. 9 years I advanced in my career, in rank, but mostly in a timely manner. I have common sense and was a good systems thinker, which helped me most in all aspects. I was able to continue doing what people did before me, filling out the ever same Excel sheets, writing and adapting the ever same offers, creating the ever same PowerPoint slides. Thanks to my systems thinking skills I could easily handle complex situations and adapt ancient processes quickly and accordingly. At least for me. In 9 years I didn’t do anything new or innovative. There was no reason for me. I tried to do what I thought was best for the project, all very context-driven (a term I also learned only a bit later about).
Urban Science – becoming a QA Lead – or not
In 2013 I finally left T-Systems and started a job in a mid-sized US company called Urban Science, active in the automobile industry’s dealership network area, that bought a small company in Munich a few years earlier, which’s product fit in their portfolio. And since coordinating and managing the testers from the US was too cumbersome, they were looking for someone to be the “QA Lead” for the Munich office. A weird chapter in my career began, that I both don’t want to miss and also would have liked to skip. I should have realized that something’s not right, when I asked to meet the team after the interview, and they came up with excuses. Of course, I didn’t ask any questions back, because I never really learned that. And I didn’t spend much time thinking further about it. Two months after signing the contract and still a few months ahead of me starting, when the boss boss from the US was in Munich, they asked me to meet him and the team. And then it became a bit more apparent why they needed me. They didn’t want to deal with one of the testers in the Munich office themselves. Well, too late.
The biggest defeat in my career to date.
One of my saddest moment in my career was the time, when I was not able to come to terms with that colleague, find a way to professionally deal with them, help them to become a better tester and more accepted in the team. But besides the lack of experience in leadership, I also lack a psychological degree, and I was not able to handle the situation accordingly. So my boss in the US and someone from HR took the short route and made them leave in the middle of my attempt to improve the situation, which is to this day still one of my biggest defeats in my career.
On a positive note, during my time there, I read loads of blog posts, magazines, a few books, and I started to actively participate in the Testing Community on Twitter. I caught up with everything that I missed over the past 10 years. I even started to be innovative. In parallel to some people in the South-East Pacific region I started to develop something that went later viral as Visual Test Model. Using a mind-map to document the system under test and guide your exploratory testing. I learned that Exploratory Testing was a thing, that it could be structured and highly useful. And that it actually was what I was doing for a long time. I even created Work Item Types in Microsoft Team Foundation Server to document it and use it more effectively. I applied Session Based Test Management very successful in the introduction of a new product. In 2015 I started designing my first test framework to finally start with automating something that was never designed to be automatically testable. I came up with innovative ideas myself, I read a lot, I discussed with people in the interwebs, I adapted a lot of industry standards. My disgruntlement with the old school approach that I was using successfully for 9 years, even got me to participate actively in some activities that I later regretted deeply. I was an active iSTQB basher and ISO29119 proponent. While I realized at some point that this iSTQB bashing was not my battle and that I should never have participated in it in the first place, I’m still against ISO29119, though could have been a bit more constructive in the process.
While trying to learn everything I could, trying out new things in the test approach, surviving long weeks, I also had to fight the long entrenched opinion in the company, that bugs found in production are a fault of the QAs, not testing good enough. I had to fight weird processes when it came to promotion for one of my team members, thanks to the US colleagues again, which later ended in them leaving disappointed. Which was a shame, because they were really good.
And it was impossible for me to find any good testers. I had about 40-50 interviews for a “manual” tester and a test automation engineer position. I’m not good at doing interviews and the company did not attract much testing talent, so it seemed. I was also only given budget for an entry level position. In the end I found one candidate for the manual job, whom I was never able to push into the right direction. My other team member, who was on my team for 3 years, was not able to understand IT basics, while having a degree in CS, and the products we tested were often too complicated for them. I tried “Leading by Example”, which was a fault in hindsight. During my time there I had to work with 10 different testers, except two, one not even assigned to the same products as me, all were lacking basics. And I was, and probably still am unable to explain basics from that low level. I think I’m doing okay when the other person brings some foundation. I was at least able to help two people advance in their skills. The others, no chance. 😦
Leading by example only works, when the group you lead is in sight of you. When they are so far behind that they can’t see where you are, they have no chance to follow. I separate the blame for that between them and me. Me because I was not able to have them catch up enough, them for not being able to tell me, that they need more adjusted guidance.
My biggest success -personell wise – in 3 years and 8 months was having an intern for a week. She was the daughter of a colleague and a very smart lady. After half a day of introduction to what software testing is and how our product basically works, she was doing an amazing job in the next 4 days. She was easily out-testing the other two colleagues working permanently with me on the products. Anyway, I realized in this job, that I was not lead material of any kind. The early defeat of having to let go a colleague, not being able to train juniors, not being able to request budget for more (senior) people, not being able to get one colleague promoted earlier than they lastly was, and other misconducts. Also, I changed line of reporting three times in three years. Nobody wanted me and my team. I can’t blame them for that.
Welcome to micro-management hell! The consequence of trying to do everything yourself.
One of my low-lights was being micro-managed by my boss. I constantly had around 100 tasks in my backlog, I was switching context one to two dozen times a day. I was never able to even finish something by more than 70%. I was lucky when I reached that much. Most tasks were cancelled before I could start them. My team was not experienced enough to help me with anything, it was disappointing. Every Monday morning in my 1:1 my boss would select the tasks for the week. I ignored them, as I had long adopted the approach: who yells the loudest gets served next. Next Monday I had then to justify, why I didn’t make progress on any of the selected tasks. Fun times.
I really liked the products I was working on though. I quickly understood the products, thanks to systems thinking again, understood the complexity of the dozens of configuration variables that often interfered with each other. I understood the majority of the 14 setups for 12 different clients on 10 different versions of the same product. The complexity of legacy data in the system was tricky but doable, at least for me. My team was always struggling. It took me way too long though to understand the basic nature of the product, not how the product worked, and how to successfully re-structure the test set in a more efficient way. But as the design of the products was very customer-driven, there was not much of a basic design and architecture understanding anywhere in the company. The developers were organized in module silos within team silos, all working on a monolith, throwing that over the fence to the QA silo to see if it actually works. One of my favorite anecdotes for that was one bug fix, where the developer came to me, telling me, they think they fixed it, but they don’t know how to get to this screen, so they can’t test it themselves. Also the ever ongoing fight to finally introduce unit tests, so that we don’t always suffer under basic blocking issues all the time. I don’t think that any unit test was even written before the day I left there. We were still on the level: “Try to see that the application starts, before handing over to QA.” Not that it was consequently followed. Playing ping-pong was our sad daily business.
I really enjoyed impersonating a Business Architect from time to time to help design solutions.
Working closely with the Business Architects, impersonating one from time to time, helped me doing something that became a pattern. I influenced and designed solutions, I partially took the decisions for architecture and design. I was actively involved in the product development. I really enjoyed this. While I was busy doing that, having calls and meetings with angry clients, I missed to take care of myself as well, and ended up in the hospital one nice December evening. From that year on, I evaluated every year (reviews and employee satisfaction surveys) compared to that event, which probably gave away who was giving that feedback, but I assume they knew anyway, as I was the only one who wrote looong texts.
Failure and Success were always very close in that company. I had a lot of colleagues I liked, a few I couldn’t stand, and I was working in an international company, speaking English all year long, which I really enjoyed. I tried hard to succeed. And as you might have already realized, I’m often slow in recognizing things. So I tried for 3.5 years to still make this assignment a success and move the company forward, before finally giving up. Giving up is probably not the right term. I learned about this approach from Sally Goble in a talk she gave at Pipeline Conf. She talked about removing the safety net from the developers by dissolving the QA department; which was the perfect solution for my context. I presented this approach to my boss and my boss boss. While my boss was afraid, probably because he nurtured the QA blaming culture for many years, the boss boss liked the approach. My suggestion to move me to the BAs, where I anyway spent most my time and the other two leftover QAs into the dev silos as embedded testers, was not accepted by my boss. As I saw this as the only way to improve the understanding for quality and responsibility in the company, I did the next best thing. I quit. That day is another weird story, but I’ll skip that. Later I found out, that my approach worked, and the devs now own more of the quality and responsibility of their modules. One of my higher-ranked US colleagues had to take over for the transition, but in the end gave up as well, for the better I’d say. This company seems to do rather well without testers.
During that time another big thing in my career happened. I went to my first testing conferences, met people I knew from Twitter in real live, gave my first conference talk and my first workshop. I started blogging. I became invested in the Testing Community. I had this theory that in 2016 I could have gone to any tester meetup anywhere in the world and know at least one person, except in Germany.
QualityMinds – Consulting, but different
In September 2016 a new chapter of my career began. I joined QualityMinds, a young and active consulting company, when I joined, mostly active in the software testing area. QualityMinds gave me the opportunity to get into consulting without the need to travel all week. And they kept their promise. Except for two workshops, my only travels in nearly five years were to testing conferences. I like my family and my home. Being on the road all week for some consulting gig, working long hours, sleeping in hotels, with the hope to have a short Friday, that is not for me.
During my time at QualityMinds a few important things happened for my career.
At my first client, I experienced the first real Agile project and probably the only one to this point. I don’t mean having stand-ups and two week sprints. I mean continuously experimenting and improving and meaning it. I was the first experienced tester on that project, and never again have I seen a team that lived the “everybody can test” mentality more than they. It was also the project where I implemented together with an intern and a newbie a UI testing framework, as I designed it in my previous job.
While I felt most of the time basically useless at my second assignment, I learned a few things. Continuous Compliance can be reality with a few basic rules to follow. That a proper BDD framework is a lot of discipline and work, but can be fun and can save also a lot of time. And I learned that also as a senior tester you can spend hours each day doing simple and fundamental tasks like analyzing nightly tests, stabilizing tests, balancing test suites, keeping dev containers up-to-date, maintaining reporting, which might not be all that important to you, but it all was important for the larger team. When I left this assignment after 18 months I soon after learned that I left a hole in the project, as I seem to have fulfilled a lot of tasks that were important for the basic stability of the project that was filled by more than a handful of people to take over what I haven’t seen as that important.
On my third assignment I felt at home from week 1 on. I mean, the interview process for that project was me in the room with two senior people of the project. And the interview began with “So, we are here to sell this project to you, so that you decide to join us.” I have to add, that this project was in a sister company of project two, and people know each other for ages there. So someone must have said a good word about me. Onboarding was a bliss. I quickly understood all the business processes, the tech stack was familiar, and the automated test set was in a good condition. I was quickly able to participate in discussions, and from time to time even influence product decisions (which is rare for an external tester). Mostly I learned to maintain, stabilize and develop the automation framework and to bring it to the next level. I was able to teach a few testers around me a few tricks. And I learned the code base in a way, that I was able to fix bugs, implement small tasks and help in any way I could to Accelerate the Achievement of Shippable Quality. After 18 months, I had to have a mandatory break, and when returning to the project, I was put in a small, agile team for implementing demo capabilities with production stability. My tasks now included by default also development tickets, which was a great honor for me. Honestly, I always thought that when I leave QualityMinds at some point in time, I would simply join this company. That was home. And the way they treated their employees including the externals was amazing. I spent in total 3 and a half years in that building.
In my last year I also had an internal assignment, joining my own colleagues for a change, at least for some time, and helping develop a project from scratch. My architecture knowledge helped me to drive decisions, establish a few basics, and overall actively participate in the project on all layers. I implemented a new kind of test framework from scratch, based on my experiences with the last three frameworks I worked with. And in general, working in a consulting company with the people who receive their paycheck from the same company as you, has a special feeling, especially after being on solo assignments for 4+ years. I enjoyed this project to the last day.
In those nearly 5 years not all was happy sunshine. Due to probably mostly self-induced pressure, I soon after joining stopped learning. Like completely. I learned on the job, but not a single bit outside. Which is especially ridiculous as QualityMinds specialized in Learning more and more over the years. I spent at least 40h a week at the client, we all had a few hours every week for team tasks, as we were very self-organized, and every now and then there was some time spend for the wider company. I was not able to find a balance, where my learning found a place. And then there was something else.
On my first day working for QualityMinds, when taking the train to the new work, I received a call from Kris Corbus; Rosie agreed that we could organize the first TestBash in Germany. This was a big opportunity, and after speaking with my CEO at QualityMinds, I agreed. Because Michael happily promised me all the support I need. And I got that, from the whole company. 13 months later, Kris and I, with a lot of help from Vera, Marcel, and Daniel, welcomed around 200 testers to the first edition of TestBash Germany. It was a success, at least from a TestBash perspective.
Personally I had to realize in hindsight, that on Saturday, the Open Space day, I had the beginning of what was a burnout. Not realizing this, not seeking professional help, trying to power through, not having a single sick day, continue to work 100% on-site at the client, ended up in around 18-24 months of mild to medium burnout, with depression, fatigue, weariness, and no energy for basically anything but the most basic functions. An approach I can highly NOT recommend. Sometimes you have to accept the fact, that something’s not okay, and that you need help.
To end this chapter on a more positive note, I also landed two talking slots again at conferences. I repeated my talk from 2016 in a worse version, where I only realized during the Q&A what potential the talk could have had. And in 2019 I gave my talk on “Testing Machine Learning Algorithms 101”, which I’m still a bit proud of. I also made the personal decision to stop talking at conferences. I don’t have many interesting things to talk about, and others are usually better at that, and most of all, because the conference stages are already full of middle-aged, white Western European cis-men, no need to stress that contingent, when me not applying raises the chance for someone else who should really be heard and who brings diversity to the stage.
You might remember from a few paragraphs earlier, that I was sure, that when the day comes where I leave QualityMinds, that it will be for this one company that I worked for 2 years in the end. Well, some things quickly change from one day to the other. I got a ping from someone who was looking for Quality Engineers (whatever that is) for this medium-sized med-tech company in Berlin, working 100% remotely. Dan Ashby told me about the product, some challenges they have, and most of all, who else was working already at Ada. And I was sold!
Ada Health – and what does a Quality Engineer do?
Exactly 10% of my career, or better, two years ago I joined Ada as a Quality Engineer. What that role means, I still haven’t found out. And I couldn’t care less. The good thing is, that all QEs at Ada interpret their role individually. Everybody brings to the table what they are good at. And if you have a rough idea of who else works at Ada in the QE department, you know they are Good, with a capital G. The nice people at Ada let me help on many levels. I am embedded in a wonderful team, with amazing colleagues. From within the team I was able to implement an end-to-end test framework for integration tests, that should serve more teams than just mine, because there was nothing appropriate in place when I joined.
I am able to support with process optimization all around releases and compliance. I inherited an awful script collection to create release documentation. In the meantime we rolled that tool out to nearly all teams at Ada and save those teams a whole lot of time during every release.
I’m doing about 2/3 of what I do in my day not because somebody asked me to, but because I think that I can help the company when I engage in these aspects. It took 18 years of good and bad experiences to reach this point in my career, where I think that these are all tasks that I can help with delivering a better product and faster and easier. Accelerate the Achievement of Shippable Quality.
Due to a necessary cost cut initiative over half a year ago, we lost a great bunch of people and had to re-organize. We slowly recover from that cut and find back on track. We got merged with another team that has lost their QE in the cut. So I’m now embedded in a team with four different modules, more than any other team here. And I’m involved in a couple of other projects on the side. We just got a new Engineering Manager through the team migration, which I’m looking forward to work with, as we lost one of the best EMs I ever had to the reorg. Our product manager who switched to us some time ago, is now finally onboarded and able to drive topics also for “our old” applications. In the rough time not only I tried my best, the whole team stepped up in the past 8 months to fill the voids and keep the team running. I can’t be more proud to be a part of this team. We were an absolute dream team before the cut, and it got proven after the cut, just how awesome this team is working together. I hope we can continue this journey in the new structures, as more changes are currently happening within our team.
And once I realized that I can’t change the company alone overnight, I embrace every baby step in the right direction. There are personal set-backs, a lot of stand still from my perspective, but from time to time baby steps. There is a lot of work still to do. And people stay willing to change. But it has to be slow and careful. So slow and careful it is. And as long as this company is willing to accept me among their peers, I will stay and from deep down in the hierarchy try to push the whole place slowly and steadily in the right direction. The right direction from my perspective, of course.
And this is my boring story up until today.
Retrospection – the bitter insights of 20 years
20 years as a tester and 3 additional years as developer (apprentice) made me to what I am today as a professional. I have found my specialization in generalization (a few years ago I called it “the broken comb“). I know a little about a lot. I can help and support in many areas. I have the foundation to quickly learn new topics, I have the ability to quickly dive deeper into topics I know a bit about already. I spoke about this in my first talk at TestBash Brighton 2016. I’m half good in recognizing Mount Dunning-Kruger, and I still suck when it comes to Impostor Syndrome. This is also very frustrating. Every skill I have, even those that I am probably good at, the chances are high that somebody around me is better than me. For years I have looked for a topic that I can specialize in, to maybe become a renowned expert for something. Besides being grumpy nearly all the time, I didn’t find anything. Every time I try to dive deeper into one topic, I get distracted (thank you ADHD) by something different that’s also interesting. There are so many interesting topics out there, that I can’t focus on just one or two. And as I stopped learning anyway a few years ago, why bother. It took me nearly 20 years to accept this fact, that there is always someone better than me. I have just so many different skills that it is impossible to be excellent at one of them. So I accept this fact now and be thankful for the experts around me and the chances where I can use my skills.
As generalist or generalizing specialist there is a huge trap for your confidence.
The broader your knowledge is the easier it is to find someone who is better than you. There are probably other generalists who know more than you. And that’s okay! Your variety is your strength! pic.twitter.com/JJQYhgyxX2
— Patrick Prill @testpappy.bsky.social (@TestPappy) May 12, 2023
As I told you already earlier, I’m often rather slow in recognizing and understanding things. For 20 years I’m keeping myself too busy, to take a step back from time to time and reflect. Slow down to speed up is so true, and I tend to not follow that advice. I also tend to not ask for advice early, I want to get shit done by myself. I don’t sharpen my axe, because there are so many trees to be felled. Last year I was asked by a wise man, why I am putting in so many hours week after week. And it was one of these rare moments where an explicit question made me step back and ponder on that fact. The answer I came up with is a complex, yet frustrating one: I’m doing this to compensate the feeling that everybody around me is way smarter than I am. As a university drop-out, though still one of the, I think, three or five best vocational training students of my year in the greater Munich area, I was the following years mostly surrounded by some very intelligent people. Since finding the testing community this number multiplied big time. In my current company it feels even more like nearly everyone they hired is an absolute ace in their area. This sense of inferiority I compensate with working extra hard and extra hours. And I know that some people say it’s not possible, but I can work effectively and concentrated for 10-11h most days. Also I tend to attract a lot of work overall. My task list is always well filled by a variety of topics on many levels. I see so many things that need optimization, and while I just said, that I’m surrounded by smart people by the dozens, I rather do the job myself, so I know that the results fit my understanding of the system and how I like them to be. I can’t delegate, so I end up with even more tasks on my desk. I just made a list of major and medium long-term topics on my desk: 14!
Speaking of complexity. As I wrote very early in this blog post, that in hindsight I found out that one of my more natural strengths ever since is systems thinking to a certain degree that seems to be better than average. I’m not great at it, but probably better than others. During my intense phase of learning from 2012-2017 I also came across the Cynefin Sensemaking Framework. I have probably not understood most of it, but the basic idea is highly fascinating. And I take from it what is useful for me. Cynefin is separating the parts of systems into domains of Obvious, Complicated, Complex and Chaos. What I learned over the years is that I felt at home in understanding complex systems. Instead of understanding first in what kind of situation I’m in, as Cynefin suggests it, I start treating everything as complex. Maybe to avoid surprises, to avoid the cliff of complacency, or because 20 years of experience told me, that systems are rarely obvious or simple, and that even complicated is an exception, especially when people are involved. Maybe that’s also the reason why giving a simple explanation for something is especially hard for me. I always try to incorporate the dependencies and complexities, to avoid that people think that it’s simple. Because often it is not.
When thinking about what I do and what I can do best, I would say laying and improving foundations. Foundations are an extremely complex topic to get right and to get harmonized across the board. Most topics I have on my plate and that I am somehow attracted to are:
detecting issues in existing processes and find missing processes
help setting things up, that should be there
boot strapping things that others can adopt or build upon
doing simple tasks that have to be done
taking care of basic tasks, the every day things that are not appealing to many
automating the basics, so less people have to worry about it and can focus their energy on other things
A lot of this is I prefer doing, because I have seen different times. I started in testing in a full-blown ITIL project. You might think of ITIL what you want, but basically any IT project that wants to be successful and maintainable will follow processes that are all somehow described in the ITIL world for ages. The way they are dealt with will differ greatly between hiring a gang of service managers and trying to manage it on the side. But not having these basic processes is not an option. Even the smallest of projects needs to deal with all of them. In 2019/2020 during my time in QualityMinds, I received my most useful training of the past 23 years. The iSAQB Architecture Foundation. This course was astonishing for me. While I was the only non-developer in the round, I was also the only one who didn’t actually learn anything new. And yes, it still was the most useful training ever. Most topics were confirmations of what I have already seen and experienced in other projects. Partially topics I have long forgotten (another phenomenon in my brain). The best thing about this course was the condensed amount of different topics that form the scaffolding and foundations of a good IT project. If I would be able to stay up-to-date and learn a bit more about several technical topics, IT architect would probably a good fit for me. And as many people around me jump onto the shiny new shit trains, the basics are often forgotten. Without good foundations and good scaffolding, the shiny new shit won’t stay up for long. And somebody has to do that job. So why not me. And when you see me on social media rooting for people talking about the basics, and being skeptic about shiny new shit, maybe you understand a bit better now why that is.
Now another weird insight of the past 20 years. I absolutely suck when it comes to explaining others the basics of software testing. I never was good at that. And I never learned or wanted to become better either. Software testing is something I just do based on systems thinking, common sense and my accumulated knowledge, analyzing potential risks. I’m not doing this in a structured manner, if I don’t have to. I don’t think that I’m a bad tester, actually I’m rather successful with the way I work for most parts of the last 20 years, but I am not a good software tester or teacher of software testing per se. I can’t teach others how I test, because it’s highly personal and complex. I’m probably a slightly above average test automator. And I’m good in testing processes and projects though. I help teams optimize their flow. I help to test and improve new and old processes. And I rather like to test ideas and make decisions early in the process, improve architecture and design of the application. I’m better at many other things that I see as related to being a software tester or Quality Engineer, while they will probably not popup on a role description and are often not expected from people around me. (some very disappointing stories could be told about this)
Accelerate the Achievement of Shippable Quality and Removing unnecessary friction
Accelerate the Achievement of Shippable Quality. I have mentioned that a few times in this blog post. This motto was introduced by Alan Page and Brent Jensen in the AB Testing podcast somewhere in the 50s episodes, if I remember correctly. Those led to the Modern Testing principles introduced around episode 70. This podcast had probably the biggest influence on me over the past 7 years. Alan and Brent find words for the things that I do, without realizing that I’m doing them. The podcast is often challenging and expressing things in a rather extreme position. But I like that, as it has always challenged my status quo. The no. 1 guiding motto for me was and stays Accelerate the Achievement of Shippable Quality. This is what I’m trying to do for 20 years to this date. Most of the time without even realizing that this is my motivation.
Guiding motto no. 2 is the statement that “improving quality is removing unnecessary friction”, that my friend Stu Crocker started making about two years ago. While the topic of quality (the term itself) is one that I come back to rather often in the past 8 years, that statement was one of my few Eureka moments. Thank you Stu for our long chats during the pandemic.
The last 20 years I tried to change projects in a way that I could do my actual work better and easier.
With every new project came the same class of problems to tackle. Every time. Mostly basics that are missing in the testing process or simply understanding how testing should be done. The need to stabilize things, making deliveries faster and more reliable. Raising the awareness of what Quality actually means. And often closing gaps in software development fundamentals that are necessary as foundation for all the other topics. Sometimes it feels like Groundhog Day.
What are you proud of about your career? This could be something that happened a while back or just yesterday.
— Ministry of Testing (@ministryoftest) June 24, 2023
Last week Ministry of Testing was asking, what are you proud of about your career? And I have to say that besides some of the achievements you read about above, I’m most proud of not quitting testing. Testing can often be very frustrating. You have to deal with software, systems or processes that don’t work. You have to be the one bringing bad news often. You have to deal with people who are disappointed. You are usually last in line, you are often forgotten, others tell you what to do or how to do your job, you have to fight for your place in the team and in the company. Testing is often seen as stupid work that everyone could do, so hiring people, teaching them for a few days and they can do the work for cheap money. Or a bit more trained people just have to write a good set of test cases, and then you can outsource the testing to even cheaper labour. Testers are scape goats. Testers are not seen as peers of any other group in a company. While all these points are not true everywhere, I have experienced them all, and I still hear stories from other testers going through one or the other of these issues just recently. Mental health is a big topic in the Testing Community for a very good reason. I’m proud to be a tester, and while I’m terrible at self marketing, or easily convincing others of better approaches, I like my job, I like the variety it brings, the possibilities. Yes, I hate to go through the same tough and cumbersome onboarding time over and over again. But in the end it’s usually worth it. I’m not a tester like anyone else, most testers I know are not. We add value to product and projects on many levels, everyone a bit different and with their own style, and I’m proud to be one of this crazy crowd.
And there is one more thing I’m proud of. Over the last 20 years I was able to find several good friends in the testing community all across the globe. People I would not have met otherwise. Several wonderful people from all over Great Britain, especially in the South and North of Wales, the East and Middle- to Northern-ish parts of England, Columbus, OH, Utrecht, Riga, Munich, Melbourne, Ghent, Bangalore, Berlin, Charlotte, NC and some other areas of North America, Lisboa, and where not. And I am even allowed to call one of the most outstanding people in this community a good friend, who happens to live in our neighboring town. And there are definitely a few more that I have forgotten to list with their center of living. I’m grateful for all of you, I hope you all know who your are, and how much you are appreciated. Your friendship is not taken for granted.
Enough ranting, enough depressing self evaluation, enough boring stories from the trenches. Time to start into the next 20 years. Whatever this journey might lead me to, I bet it will be to help improve foundations to create better solutions and ship them faster.
And sorry for the long post, if you made it to this point, here is a potato.
When you are working with APIs and especially testing them, then there’s usually also the step of building some JSON request and asserting the response. And it often means complicated JSON structures with nested elements, lists, and all kinds of scurviness. The focus of today’s article is on the Asserters.
Disclaimer: All samples are in Java code, but can most probably be transferred to most other languages as well.
Disclaimer 2: I haven’t seen this pattern documented or used anywhere else yet. So if you are aware that this is already somewhere described and shared, please let me know in the comments!
The Problem
Let’s take an example from a simple CRM system that manages customer contacts. Here we retrieve the contact information for the Sample Company with ID 123.
In my past 10 years since first getting into contact with APIs, I have seen a lot of test code that looks like this, and I have probably written my fair share of it.
@Test
public void firstTest() {
Company company = MagicCRMRestRetriever.getContact(123);
Assertions.assertEquals(123, company.id);
Assertions.assertEquals("Sample Company AB", company.companyName);
Assertions.assertEquals("Main Street 1", company.address.address1);
Contact salesContact = company.contacts.first { it.role == "Sales" };
Assertions.assertEquals("Gary", salesContact.contactName);
Assertions.assertEquals("Henderson", salesContact.contactSurname);
Phone salesContactMobile = salesContact.phone.first { it.type == PhoneType.MOBILE };
Assertions.assertEquals("+43123456789", salesContactMobile.number);
}
And this is just a small example of an assertion orgy. When I first learned about the pattern I’m about to introduce to you, the project context was Airline industry, and the JSONs contained flight and ticket information. We talk about at least 100-150 entries, easily. Imagine the big blocks of assertions.
Those of you who say, just use a comparator, create the payload you expect and compare it with the one you get. Well, I don’t care about all fields in all cases. And that makes it easy to get distracted in the test you write. I want to focus on the fields that are of interest to me. See for example the createdAt and updatedAt, and IDs of the contacts and address fields.
The Solution: Fluent Asserters
Already in 2005 Martin Fowler wrote about the FluentInterface. But as my main focus from 2003 – 2012 was on Oracle PLSQL, SAP ABAP, Excel VBA, and other non-sense I have never seen again since, my knowledge about design patterns was a bit outdated. One of the developers in the aforementioned project came up with the nice idea of introducing Fluent Asserters. Attention: This is not the same kind of fluent assertions that are possible with e.g. AssertJ, where you can chain assertions on the same variable.
First I will show you how sweet these Fluent Asserters can look in action, before I then scare the hell out of you about implementation details. Here is the sample of the same test as above.
@Test
public void firstTest() {
Company company = MagicRestRetriever.getContact(123);
AssertCompany.instance(company)
.id(123)
.companyName("Sample Company AB")
.address()
.address1("Main Street 1")
.done()
.contacts()
.byFilter(c -> c.role == "Sales")
.contactName("Gary")
.contactSurname("Henderson")
.phone()
.byType(MOBILE)
.number("+43123456789")
.done()
.done()
.done();
}
Doesn’t it look nice and neat, with all those indentations, that let it look a bit like a beautified JSON file.
If you like what you see, it might be worth to read on and get through all the nasty little details necessary to implement this kind of asserters. If you don’t like it, you better stop reading here, if you haven’t done already so.
The Asserter Interface and Chained Asserter Interface
The basic problem that needs to be solved is the chaining of DTOs. In our simple example above, the JSON is combining four different DTOs. And if you have a good API design it might be that certain DTOs are re-used all over the place. How do you distinguish which one to use. Well, you don’t have to, when all DTO asserter classes inherit from the Asserter Interface and Chained Asserter Interface.
public abstract class AbstractChainedAsserter<TItem, TParent extends Asserter> implements ChainedAsserter<TItem, TParent> {
private final TItem item;
private final TParent parent;
protected AbstractChainedAsserter(final TItem item, final TParent parent) {
this.item = item;
this.parent = parent;
}
@Override
public TItem get() {
return item;
}
@Override
public TParent done() {
return parent;
}
}
The ChainedAsserter is the heart piece, because it allows you to return back the parent (done()) in a way that the Asserter doesn’t need to know what type that parent is. This allows to embed the same DTO into several others without the need to distinguish between Asserters, because the parent is set on navigation through the DTO. As long as the parent is, of course, also an Asserter. The highest element must always be an Asserter.
The Implementation of the Asserter
When it comes to the actual implementation, the Asserter has to extend the AbstractChainedAsserter.
public class AssertCompanyDto<TParent extends Asserter<?>> extends AbstractChainedAsserter<CompanyDto, TParent> { ...
The TItem is the DTO to assert itself, and the TParent is another Asserter. Every Asserter has two methods to instantiate it, instance(target) and chainedInstance(target). instance is called in the actual test, when the Asserter is instantiated with the actual variable to investigate and assert. The chainedInstance is called only by Asserters, when they hand over to another Asserter. Both methods call the constructor, instance is not setting a parent, as it represents the highest node of the JSON, and chainedInstance is setting a parent. The constructor also asserts that the handed over object is not null.
public static AssertCompanyDto<Asserter<CompanyDto>> instance(final CompanyDto target) {
return new AssertCompanyDto<>(target, null);
}
public static <TP extends Asserter<?>> AssertCompanyDto<TP> chainedInstance(final CompanyDto target, TP parent) {
return new AssertCompanyDto<>(target, parent);
}
private AssertCompanyDto(final CompanyDto target, TParent parent) {
super(target, parent);
assertThat(target).describedAs("Company must not be null").isNotNull();
}
Standard types like String, Int, Date, etc. can directly be asserted. And as it is a fluent interface type of implementation, the method has to return the Asserter itself (this).
public AssertCompanyDto<TParent> companyName(final String expected) {
assertThat(get().getCompanyName()).describedAs("companyName").isEqualTo(expected);
return this;
}
Chaining Asserters
When an attribute of the DTO is another DTO, then the according method is not expecting an object as comparison, but returns the Asserter for the nested DTO. While you can implement also a simple object comparison method that handles comparing the whole DTO at once, I have rarely used them, and hence dropped them in later projects. Now we see the chainedInstance in action. The return type is defined with itself as parent. Also we directly check that the object is not null, so that we actually have something to assert.
public AssertAddressDto<AssertCompanyDto<TParent>> address() {
final AddressDto target = get().getAddress();
assertThat(target).describedAs("Address").isNotNull();
return AssertAddressDto.chainedInstance(target, this);
}
List Asserters
Some of the embedded DTOs might even be lists. In our example that is contacts and the phone numbers of the contacts. Lists need to be treated specially. Because we want to navigate in lists, select specific elements (byIndex), e.g. when you know exactly which element should be on first position, or you want to search for an entry based on a known attribute (byFilter). Or simply know how big the list should be (size).
public class AssertContactDtoList<TParent extends Asserter<?>> extends AbstractChainedAsserter<List<ContactDto>, TParent> {
public static AssertContactDtoList<Asserter<List<ContactDto>>> instance(final List<ContactDto> target) {
return new AssertContactDtoList<>(target);
}
public static <TP extends Asserter<?>> AssertContactDtoList<TP> chainedInstance(final List<ContactDto> target, TP parent) {
return new AssertContactDtoList<>(target, parent);
}
private AssertContactDtoList(final List<ContactDto> target) {
super(target, null);
}
private AssertContactDtoList(final List<ContactDto> target, TParent parent) {
super(target, parent);
}
public AssertContactDtoList<TParent> size(final int expectedSize) {
assertThat(get().size()).describedAs("expected list size").isEqualTo(expectedSize);
return this;
}
public AssertContactDto<AssertContactDtoList<TParent>> byIndex(final int index) {
assertThat(0 <= index && index < get().size()).describedAs("index must be >= 0 and < " + get().size()).isTrue();
return AssertContactDto.chainedInstance(get().get(index), this);
}
public AssertContactDto<AssertContactDtoList<TParent>> byFilter(final Predicate<ContactDto> predicate) {
final ContactDto by = by(predicate);
assertThat(by).describedAs("list entry matching given predicate not found").isNotNull();
return AssertContactDto.chainedInstance(by, this);
}
private ContactDto by(final Predicate<ContactDto> predicate) {
return get().stream().filter(predicate).findFirst().orElse(null);
}
}
So, when dealing with a list element in your DTO, then you don’t directly return the Asserter of the DTO, but first its ListAsserter. This following method is part of the AssertCompanyDto class.
public AssertContactDtoList<AssertContactDto<TParent>> contactList() {
final List<ContactDto> target = get().getContacts();
assertThat(target).describedAs("Contacts must not be null").isNotNull();
return AssertContactDtoList.chainedInstance(target, this);
}
Extend as you wish
And you can of course extend the Asserters with anything that you need often. An example is that often you might not want to compare a String exactly, but only check that it contains a certain substring. So why not add to your normal exact comparison method a “Contains” method.
public AssertCompanyDto<TParent> companyNameContains(final String expected) {
assertThat(get().getCompanyName()).describedAs("companyName").contains(expected);
return this;
}
The Secret to success: write a Generator
Disclaimer: I cannot share any code as of now, as the generators that I have produced so far are part of special code bases that I cannot publish. If interest is big enough though, I will try to re-create one in a public GitHub project. Leave me a comment, so that I know there’s interest.
The code described above is complicated and producing it manually is error-prone. And also it’s stupid work. Nobody likes stupid work, except maybe me sometimes. So the easiest way is to write a generator that analyzes the given DTO and creates the Asserters and ListAsserters auto-magically.
The generator is using the power of reflection by looking into the innards of the class, analyzing what it consists of.
The generator needs to do basically do the following things:
scan the parent class provided as input
find all attributes, and in case of Java e.g. also determine the getter
find out if the type of the attribute is a standard type (String, Int, Double, etc)
find out if the type is a custom type (in our example above, e.g. AddressDto)
find out if it’s a list
maps also work, but I haven’t found a proper way to automate that yet
custom types need to be scanned and analyzed (yes, recursive call)
manage the scanned classes, to avoid multi-scanning
you should also check which Asserters you already have to avoid unnecessary work
after collecting the work that needs to be done, start creating the Asserters and ListAsserters
use a template engine to provide the standard content for each class
add for each attribute the methods you want to add
at least the 1:1 comparison
String contains
timestamp with a potential diff (so that you can compare timestamps you only know roughly)
number formats with smallerThan or largerThan
and so on, whatever makes most sense in your project to always provide out-of-the-box
write the files to folder
Moving the files to their target directory is then again a manual process, to check that everything worked as expected. Then you can immediately start writing your tests with beautiful fluent assertions as shown on top of this post.
In case any DTO changes, just re-run the generator and it will add or remove changes. Be careful though, when you added custom assertions to your Asserters. IDEs with proper version control support should be helpful here to restore the according methods easily.
Famous last words
I am very sorry. This was now a very quick and dirty description of what the generator needs to do. But going into detail would be way too much for this anyway way too long blog post.
I hope that you understood the basic concept of the fluent asserters and find them as useful as I do. I don’t want to miss them anymore in any project where JSON/DTOs are getting a little bit bigger. It makes the tests so much more readable, writeable and maintainable. It focuses the tests on what is really important and not clutters them with technical details.
Quality is a complex system. Quality is all about roles, relations, context, budget and the question who owns problem.
If you read this blog post, most probably your personal understanding of “quality” is anyway more advanced than mine. I still try to catch up with about 33 years of stubborn ignorance. So feel free to stop reading at any point, if you come to the conclusion: “Well, yes, duh…”
There is something that bugs me now for a few years. It’s the way people treat the term “Quality”! And I used to be one of them. I have tried many times over the past 5 years to come up with a good and comprehensible explanation of the issues that I have with it. Attempts 1 – 3 ended up in draft mode. This is attempt number 4 and 5! It was supposed to be simple and short and spot on. Guess what: it’s not!
Throughout my life and my career I have met people and seen many examples where quality is treated as a simple thing. It only happens since a few years now, that I’m kind of allergic to sentences like: “This t-shirt is of good quality!” Is it really? Why? What exactly do you mean? For whom? You want to buy it?
In a professional context my goal for *cough* years is to help improve “quality”. Just do this and it will become better, they said! Since several years now, I treat these statements with more respect, and many times I begged to differ, and here is why!
When you look at the five word long definitions for “Quality” from some famous people, you could think it is actually a simple thing. “Quality is conformance to requirements” (Philip Crosby), “Quality is fitness for use” (Joseph Juran) or “Quality is value to someone” (Jerry Weinberg). Sounds easy, doesn’t it? The problem is, that behind those easy statements hides a complex system, that we too often neglect for the sake of simplicity and the urge to be under control.
TL;DR: Quality is all about roles, relations, context, motivation, budget and the question who actually owns problem.
I want to explain now why I think Quality is a complex system.
Quality describes the result of an evaluation of a solution. When you think about it, every thing that mankind produces or uses is trying to solve some kind of problem. This solution might be something real or virtual, it might be a thing or a process to create a thing. Every thing tries to solve a problem. Quality is used as a way to describe how good this solution is.
The standard of something as measured against other things of a similar kind; the degree of excellence of something.
A distinctive attribute or characteristic possessed by someone or something.
That is rather vague and imprecise, as it’s not naming any property it refers to explicitly. The term “quality” is actually not able to state anything by itself. You either need to set it into relation to something comparable (#1) and you need to be more specific about the relevant characteristics of the something that you want to refer to (#2). The term “quality” itself means nothing and everything at the same time. Quality refers to individual characteristics of something, but also means the sum of them all.
And guess what, despite the strong use of the term quality, there’s nearly always a shallow agreement on what it actually means. We use a simple word to refer to a subjective evaluation of a complex system. That has to go wrong…
What means complex?
I’d like to borrow the description of the domains of the Cynefin sense-making framework.
Unknown – is the state when you don’t know yet in what domain you are.
Obvious/Simple (Sense – Categorize – Respond) – When you do A, B will be the obvious result. A simple and clear set of rules describes what happens in the system.
Complicated (Sense – Analyze – Respond) – When you do A and B, C will follow, as long as D is fulfilled. You are still able to formulate rules that describe what will happen in a system. They are just more – well – complicated.
Complex (Probe – Sense – Respond) – You do A and B to change C, now also D and E happened. You are able in hindsight to understand why D and E happened. The next time you do A and B, C might change again, but if D and E will also happen or not, you probably still don’t know!
Chaos (Act – Sense – Respond) – D happens, you don’t know why, you try A, B, and C. Eventually something about D changes. If you try it again it doesn’t. Now E happens. Where did that come from?! Aaaaah, PANIC MODE! Stay calm, try to understand where you are, get back into the complex domain. Get some control back.
An important aspect about Cynefin is, that it doesn’t try to describe one state for the whole thing. It tries to help you understand which aspect of some thing should be seen in which context, which in turn helps to understand how to treat it. You don’t treat something complex the same way as something simple. You can try, but you have been warned, there might be consequences you haven’t thought of! (Careful, Chaos waits behind the cliff of complacency, if you treat things as too simple!)
The different aspects of quality should be treated with respect to their domain.
What is a system?
According to Oxford Dictionary A set of things working together as parts of a mechanism or an interconnecting network; a complex whole.
According to Wikipedia: A system is a group of interacting or interrelated elements that act according to a set of rules to form a unified whole. A system, surrounded and influenced by its environment, is described by its boundaries, structure and purpose and expressed in its functioning.
In my opinion Quality fulfills all the aspects of a system. A statement about quality is not “just” one single thing, even if it is often treated like that, because we think that we “just” evaluate the quality of a single thing. It’s actually taking into account a list of properties, people, relations, motivations, experiences, and context. People who are involved in the system, all have a different relation to the thing itself and also to all parts that are involved. There are feedback loops between properties that influence each other, changes to the system following different motivations. Each influence changes the system. Many moving parts, many influences from the in- and outside. Relations and motivations you didn’t think about before! Boundaries might change, sometimes unexpectedly. How big is the system you are looking at? Is it the relation with one single person, or even a long list of people. Are the people all following the same or rather a similar motivation, or do we have properties or key players with competing motivations? Can you look at people individually, or do they have a relation in regards to the thing. What about the process involved to create the thing. Or is the thing a process? If I evaluate the quality of a thing once, will it stay like that? No, because time elapses and contexts change. Important: Quality is not even a closed system!
Quality is a relationship between a person and a product/process that varies over time. Yet we usually don’t treat the word as such.
Relations and subjectivity make the difference
What is your relation to the thing and its system, which’s quality you want to describe? There are objective criteria to evaluate quality, yet we don’t know about the personal order of preference of everyone relevant involved. We don’t know how much weight each aspect of the thing adds to the individual’s evaluation of quality. Do you see it the same? Probably not! Even if you refer to one quality characteristic specifically, your evaluation might still differ. That’s not the only subjective part, when looking at quality. There can be many relations between quality criteria of a thing and the person using the thing. These relations can be obvious or obfuscated. (My daughter uses an old pair of compasses for math. She prefers that one over the newly bought, because it’s her mother’s. To anyone else, it would just come down comparing two pairs of compasses, that special relation is missing in their evaluation.) Relations are the key aspect in my opinion that make the system of quality complex. You cannot understand ALL of them, so you can never predict what will actually happen in this large net of intertwinediness .
Who owns the problem?!
Like aforementioned, every thing used and produced by mankind tries to solve a problem. That even includes the process to produce things. Some problems are more obvious than other. (A fork is usually kind of obvious. What about a piece of decoration for the mantelpiece?) When we want to evaluate quality, we need to know, what part of the solution are we looking at, who owns the problem, what is their relation to problem and solution, and what is their motivation to solve that problem?
When the potential consumer base is large, you can’t create the perfect solution for each individually. To reach the maximum audience, you need to find a way to disappoint as few as possible. The system has to reach a certain level of balance.
What is your role?
Do you own the problem and want to solve it, so you evaluate potential existing solutions or proposals? Are you aware of a problem and want to solve it? Whom do you solve it for? For yourself, for someone else, someone you know and have access to for feedback, for yourself and others, only for others? In case more than one party is involved, is the problem really the same? Is everybody feeling the same about the relevance of the problem. Does everybody think that the proposed solution is a good solution? Is your solution competing with others?
Motivation and budget
Someone wants to solve a problem and evaluates one or more solutions and comes to a decision about the best candidate available. But there are other aspects attached to the problem as well: motivation and budget! Do I want to solve the problem? Can I and do I want to afford this thing to solve my problem. This is not only about money, also about time, space, and everything else that is scarce (which is basically everything!). Is solving the problem worth to acquire this solution?! Is my motivation to solve the problem so high, that I take the next best solution? Do I want to spend the amount and effort? If the person who owns the problem decides: No! Then your solution is not a solution, but only a proposal. If your interest is to “sell” your solution, you need to keep that in mind. And it’s not only about money. The “price tag” might also mean prestige, or positive feedback that rewards you.
There is also the situation that someone has to use a solution that someone else decided to be valuable for solving a problem. Think about the software system you have to use at work. Think about the pullover your mother bought you when you were eight. How does the evaluation of quality change for you and them. What role does the budget play?
From a kind of business perspective, the only quality evaluation of the thing that counts, is the one from the acquirer. You need to convince potential consumers that your solution is worth it. (That’s the reason for shiny marketing brochures to convince management, not the actual users!) I can produce the best solution in the world. If I don’t find a problem owner who wants to use it, it’s probably worthless. I can think highly of a certain product/process and praise it. As long as I don’t have a problem that it fits to or I just buy it because, you know, reasons…, what is my role? I may be an influencer for someone who has to make a decision about acquiring a solution, and suddenly I’m part of the system and my opinion counts.
Summary
Working effectively in the “Quality” domain means to understand and never underestimate the system’s complexity. You should know about as many relevant aspects, and be able to predict what could happen when you change a thing. For example, adding security to anything usually results in reduced performance and usability. Are your consumers really preferring additional security over speed and ease of use?
You need to understand the relations between quality aspects as well as their relation with stakeholders, as well as many other relations, like for example between a potential user and the producer. Is the brand image important to a potential consumer of the solution? Is “having” more important than actually solving a problem? Know and understand your audience!
You need to understand the problem and people who have the problem. You need to help understand the impacts on quality in case of certain changes. You need to balance the quality and the resulting price tag with the budget of your potential users. You can put gold plating on a sledge hammer. But don’t prepare for mass production just yet! The quote “Just because you can, doesn’t mean you should!” comes to mind. Both from an ethical and a business perspective.
Be warned, I might add a second part to this article using a couple of real world examples and explain the difficulties when it comes to evaluating quality.
When you are in a dialog, especially in a written form, E-mail, Twitter, messenger service and you are answering to someone and your sentence starts with “But”: Delete the sentence and rephrase it.
“But” is often a dialog killer, virtually taking back what you just wrote, and also a hint for mansplaining happening.
“I don’t want to tell you what to do, but…” – I tell you now what to do.
“This is a good idea, but…” – It actually is a bad idea.
“I really like you, but…” – Get lost!
“Well, actually…” – The new “but” of too many men.
“Have you thought of…” – tough balance here – it can spark new ideas or simply mean “I know you are stupid, so I help you think”.
When you try to say/write/think “But”. STOP! Delete the sentence! When you already said it, excuse yourself and start to rephrase. Think about what you really want to say. Think about the person you are communicating with. Think about the context.
Do you really want to kill the conversation, make a decision for someone else or tell the other person that they most probably didn’t think about this very important aspect that only you know? In most cases, I don’t think so.
Use “Yes, and…” instead. Try to be more supportive in your statements, don’t make the other person feel that you think they are stupid – even if you probably do! That is a sign of respect!
Communication is hard!
So, the next time you write “but”, think of me sitting on your shoulder, looking batshit grumpy at you. Stop typing, say sorry, delete the sentence, THINK, rephrase. Respect! That is what counts in most if not any conversation.
Trying to get my bike fixed taught me an interesting lesson about balancing quality aspects and keep the greater system in mind, when optimizing things. The manufacturer changed their wheel design, but forgot the maintenance aspect.
This last week I learned a rather interesting lesson about quality and systems thinking. To set the context:
End of June I bought myself a new bicycle for the first time in 20 years, a road/race bike. When looking for potential models I focused on one set of components (brakes, groupsets and all that). Regarding frame and wheel set I had no preference. Buying a bike in 2021 is anyway a rather hopeless endeavor. I was very lucky, when I found a shop in Ingolstadt (about 85km away) that had one in my frame size in stock.
When riding through the “outback” last week, I hit one bump and heard a crack. A spoke broke and my tour that day was done. I called my wife to pick me up. As someone with an aspiration to be a maker, I wanted to fix it myself. I had done it before (truing more often, replacing a spoke ages ago, but okay), so why not this high-tech thingy of a wheel. While waiting, I was searching on my mobile for replacement parts. There actually was a maintenance set by Newmen with a set of spokes, nipples and washers on the market. Only problem, it was sold out in all shops that I could find.
To save me some time – how funny that sentence is, only came to my realization 7 days later – I decided to bring the wheel to one of the bike repair shops in the vicinity. On Saturday I tried one in my town, who also sells race bikes. They didn’t have the right length of spokes in stock and were about to go on vacation. The one in the next town didn’t have one exactly the size, but wanted to try it. On Tuesday he called me, that it didn’t work. Ordering replacement parts was not in his interest, as he has to order them by the 100. I called a few more bike shops, but no success.
So I started looking again on the internet. I found the right length of spokes (as pack of 20), and got offered also the right tools I needed under “What other customers were also buying”. 3 days later the parcel arrived, exactly 1 week after the incident. I took the Friday afternoon off to fix the wheel.
I removed the rim tape, was able to shake out the nipple with the broken spoke threading, and put the wheel on the truing stand. I checked the packs of spokes, and the longer ones seemed to be the right ones. Only the nipples looked different. The ones that came with the new spokes had a slit on the side where they should hit the rim. That slit fitted to one of the fancy special tools that I ordered. But that way around the tool was useless. So I checked the spoke wrench. That one didn’t fit as well. WTF? Never blindly trust an algorithm!
I was grumpy – for a change – took one nipple as example and drove to a couple of bike shops (5!) to find a fitting spoke wrench. I haven’t seen so many confused faces in a while. One was even mansplaining me, that I am wrong, and that’s not the way these things are supposed to work, and the tool I’m looking for doesn’t even exist. At the last shop on that round I had a nice chat with the boss, chatting about shortage of materials on the market and other things. Also he didn’t know about that way of mounting nipples. But he gave me the tip to call the manufacturer.
On my way home, already driving around through the county for over 90 minutes, I decided to call the shop in Ingolstadt first. Luckily the guy picked up the phone who had sold me the bike. A true bike freak. I told him my problem and he immediately understood and told me that the wheel set had to be sent to the manufacturer. I should come by and they would arrange a replacement wheel set, until mine is coming back from the manufacturer. So, back into the car, driving 1h north to Ingolstadt, and finally all pieces of the puzzle came together.
Someone had an idea
Someone at Newmen had a fancy idea. To achieve a cleaner look on the rims, they basically turned the nipples around to fix the spokes from inside the rim.
Here is a rough sketch to illustrate the idea. Newmen produced nipples with a clean head (no slit) to prevent shaving off the rim, when screwing the spoke in. To do that, you need a long tool with a slim head and a 3mm square profile to reach the nipples inside the hollow chamber of the rim (an inside nipple wrench). Also the holes on the rims are much smaller, as only a 2mm spoke has to fit through it. Nice idea!
Basically the Rest of the World (ROW) is doing it differently. They drill a bigger hole in the rim, and fiddle the nipple through that hole. The nipple with its square profile is then outside of the rim and reachable by the standard nipple wrenches. That also explained the slit on the nipples I received and the special tool to fiddle them in.
And this design change explained everything I have experienced the past week. The stunned faces, the material shortage, the inability to find someone able to help me.
The Balance of Quality Criteria
What the manufacturer did was underestimating the right balance between two quality criteria. The two competing aspects in this case were “Look/Attractiveness” and “Maintainability”.
Yes, hiding the nipples inside the rim created a cleaner, crisper look.
BUT! That means trouble for maintenance. And here systems thinking comes into the picture. When you look at one thing to improve its quality, you have to keep the environment/system that it is running in, in mind. In case of Newmen, they forgot the bike repair shops and people maintaining their bikes themselves.
Because not only to change entire spokes, but also to simply true a wheel, you need to remove the tires, tubes, and rim tape. Truing a wheel, mounted on the bike, is basically impossible that way. And you need special tools, that many shops don’t even know about (5 out of 6 in my vicinity). And also special spare parts, as the nipples need to be without a slit.
In manufacturing this is probably not too much of a change, as you have a naked rim on a truing stand in front of you, and where from to apply the tool to change the tension of the spokes is just a matter of exercise and training.
As long as the wheel is intact and functioning 100%, all is fine. But as soon as you face an issue, all that beauty is getting in the way of quality!
Newmen has accepted that fact and will most probably / hopefully now redrill my rims and re-equip them with standard nipples, ones that look out of the rim. I was told by the bike salesman, that they had seen the problems happening quite fast, once the wheels were out in the field and changed their design mid-year of production, which rarely happens in the bicycle industry. As a side note, I also don’t expect that the maintenance set for that rim to get back in stock.
When I buy my next bike, I know now one more quality aspect to look for. But now I wait for my wheel set to return in a few weeks.
TL;DR: Implementing Test Automation (from a test case and framework perspective) are a great place to do exploration on several layers of the SUT. If you are just there to automate a test case, you miss so many chances to improve the system.
This tweet from Maaike Brinkhof inspired me initially to come up with this post:
Well fuck it I'm just gonna say it. I don't follow "a process" during testing, I just use common sense and see where that gets me.
That is addressing a topic that crossed my desk quite some times lately. When preparing a talk for our developer CoP at QualityMinds, a colleague asking me for advice how to structure their test approach, or one of my teams at my last full-time client engagement, where in the planning session for every story the same three tasks were created for QA. 1. Test Design, 2. Test Automation, 3. Manual Execution + ET.
As you might have already read about it, my understanding of “testing” is probably a bit different from others. What I want to describe today is a bit of a story how my actual testing looks like, when I am in a test automation context. Over the past couch*cough years I learned to enjoy the power that test automation provides for my exploratory testing on basically all layers of the application.
Disclaimer: I didn’t work in a context where I have to automate predefined test scenarios in some given tool for quite some time now. For the last 8-9 years, I worked embedded (more or less) in the development teams, mostly also responsible for maintaining/creating/extending the underlying custom test framework.
Test Automation provides for me the opportunity to investigate more than just the functionality, because I have to deal with the application on a rather technical level anyway. This way I can make suggestions to the system architecture, look at implementation, do a code review, clean up code (you probably know those colleagues that always forget to put a “final” on their variables causing lots of useless Warnings in the SonarQube) and understand better how the software works. Blackbox testing is okay, but I like my boxes light grey-ish. This can save me a lot of time, by just looking at if-statements and understanding how different paths through the functions look like.
Unit and integration tests were mostly part of the devs’ work, but when I preferred that level, I also added more tests on that level. But most of the time I implement tests on service and UI level.
I start with an end to end spike for the basic test case flow for the new functionality. This helps me in creating the foundation of my mental model of the SUT. And I also see first potential shortcomings of the test framework. Things like missing endpoints, request builders, elements in page objects or similar UI test designs and so on. First issues with testability might appear here, if according endpoints are missing, access to some aspects is not given, and whatever you can come up with in the current context that would make testing your application easier. So either go to the devs to let them improve their code or do it yourself and let them know what you did. (If you do it yourself, the second part is important! That belongs to communication and learning!)
This is also the moment when I most often look for implementation details, if they are consistent, logical, complete and usable. That goes for endpoint names, DTOs for request and response, REST method, UI components and all that. <rant>Especially these dreadful UI components, when for the same application the third or fourth different table structure is implemented, which makes it impossible to keep a consistent and generalized implementation for your UI test infrastructure, because you need to take care of every freaking special solution individually.</rant>
Once the first spike is implemented we have a great foundation for a standard test case. Now we can come up with the most common scenario and implement it. The happy path, if you want to call it that way. I try to put in all necessary checks for the SUT, that all fields are mapped and so on.
At that point I also come up with the first special scenarios I want to check, if they are not anyway already mentioned somewhere in the story description or test tasks. So I continue building up my test case and try some variations here and there, and compare the results with what I expect, what the requirements state (not necessarily the same thing), and how data passes through the code.
I tend to run my tests in debug mode, so that I can see what different variables hold. Additional request parameters, additional response fields, unnecessary duplication of information. That often gives me also more insights in the architecture and design. Why are we handing out internal IDs on that interface? Is that information really necessary in this business case? Why does it hold information X twice in different elements of the DTO? Can we probably remove one of them?
I also like to debug the application when running my test case. Do we pass through all layers? Why is that method bypassing the permission check layer? Do we need special permissions? Ah, time to check if the permission concept makes sense! This step is converting from one DTO to another? Couldn’t we then just take the other DTO in the first place? Persistence layer, hooray! Let’s check for null checks and according not null columns in the database. Did the developers forget something? I might not be able to pass null on that parameter via the UI, but probably through the API directly?
I found more scenarios, all similar but not the same. Can we simply add a parameter table to the test and let run one implementation multiple times? What would the difference be? Do I really need to add test cases for all of them? What would the value be?
<rant>Recently I had an assignment to analyze some implementations for a customer. And there was this one(!!!) method that took care of 26 different variants of an entity. There wasn’t even a unit or integration test for it. They left it for QA to check it in the UI end-to-end-tests or manually! 26 scenarios! That is a point, where I as a tester go to the devs and ask if we could re-design that whole thing. Is that out of my competency? I don’t think so? I uncovered a risk for the code base, and I want to mitigate that risk. And mitigating it by writing 26 different test scenarios is not the way of choice! So stand up and kick some dev’s butt to clean up that mess! </rant>
I send in request A, and get back response B. Can I simply evaluate response B that the action C I wanted to trigger happened or did the endpoint just enrich response B with the information from request A and didn’t wait for action C to actually do something? Trust me, I have seen this not only once! I also have seen test cases where the author checked the request for the values they set in the request, because they mixed it up with the response?
Back to action C! How can I properly evaluate the outcome of action C? In the past years I had several projects where you always found proxy properties of the result that you could check. This is a bit like sensors in a particle accelerator. You won’t see or measure the actual particle that you wanted to detect, but the expected result of an interaction with other particles. This often happens in software testing, too, when it’s not possible to observe all entities from the level of your test. Request A triggers action C, but you don’t trust response B. You rather check for result D that action C will have caused if everything worked properly. This actually requires a lot of system thinking and understanding.
Then comes the part where I “just” want to try out things, that some call exploratory testing, some call it ad-hoc testing, I call it also testing, as it’s just another, important part of the whole thing where I try to gain trust in the implementation. Anyways, so I take some test scenario and play around with it. Adjust input variables, add things, leave out things, change users, or whatever comes to mind. You know this probably as “Ha, I wonder what happens, when…” moments in your testing. I might even end up with some worth-to-keep scenarios, but not necessarily.
Earlier this year I also had the context that I was adding test automation on unit and service layers for a customer-facing API. So basically in the service layer tests I was doing the same thing that the customers would do when integrating this API. I was the first customer! And thanks to some basic domain knowledge I could explore the APIs and analyze what is missing, what is too much, what I don’t care about, etc. I uncovered lots of issues with consistency, internal IDs, unnecessary information, mapping issues, and more, because I was investigating from a customer perspective and not just blindly accepting the pre-defined format! This was exploratory testing at it’s best for my perspective in that context!
When I implement new automated test cases, I also always test for usability and readability of the tests. So when implementing a scenario and for example the test set-up is too complicated to understand or even create, then I tend to find simpler ways, implementing helpers, generalize implementations to improve those aspects of the test framework for the new extended context it has to work in.
As some of the last steps of implementing automation I go through the test cases and throw out anything that is not necessary and clean up the test documentation parts. I don’t want to do and check more than necessary for the functionality under test, and I want others to understand it as good as possible. Which I have to say is often not that easy to achieve, because I tend to implement rather tricky and complex scenarios to cover many aspects. As I mentioned before, I’m a systems thinker and systems tend to become rather complicated or even complex rather quickly and I reflect that in my test cases! Poor colleagues!
Some might call this a process. Well, if you go down to terminology level, everything we do basically is a process, but it doesn’t necessarily follow a common process model. Because when we refer to “a process” in everyday language, like Maaike in the tweet I mentioned on top, we usually mean “a process model”. And this is why I totally agree with her. And some people who ride around on the term “process” don’t simply understand the point she wanted to make! Context and exploration are so relevant and driving forces for me, that it’s impossible for me to describe a common process model of what I do, and that common sense (I know, not that common anymore) and my experience help me most in my daily work and not using some theoretical test design approaches. Test Automation, Automation in Testing, Exploratory Testing and Systems Thinking in general all go hand-in-hand for me in my daily work. I don’t want to split them and I don’t want three different sub-tasks on the JIRA board to track it!
I’m just not one of these types that read a requirement, analyze it, design a bunch of test cases for it, implement and/or execute them, and happily report on the outcome. Of course I come up with test ideas, when I read the requirement. And if I see tricky aspects, I mention them in the refinement or planning, that they can already be covered by the devs or rejected as unnecessary. When I actually get my hands on the piece of code, I want to see it, feel it, explore it. And when I decide that the solution dev came up with is okay and actually what I expected it to be, then I’m okay to stop further testing right there.
When I started writing this article, some weeks ago, I was made aware of that Maaret Pyhäjärvi also wrote about the intertwining of automation and exploration. You can find for example this excellent article for Quality Matters (1/2020). And there’s probably more on Maaret’s list of publications. And probably other people also wrote great posts about this topic. If you know any, please let me know in the comments. But I wanted to write this post anyway, to help myself understand my “non-process” better, and because some people on LinkedIn and Twitter asked for it. And probably it adds something for someone.
This post is an explanation how I see Test Automation. There is nothing new in here, and you probably have read that all somewhere else. I just want to create a reference in case I need to explain myself again.
Over the past few days I wrote some blog post on the test automation pyramid, or rather the band-pass filter. And I wrote about the trust in testing. This lead to some discussion and misunderstanding, hence I decided to write this post.
This post is about my opinion and my take on test automation. Your opinion and experience might differ, and I’m okay with that, because – you know – context matters. You might also prefer different names for the same thing, I still prefer Test Automation.
Disclamier and Differentiation: This article is about test automation, which means tests that have been automated/scripted/programmed to run by themselves without further user/tester interaction until they detect change (see below for more details on this aspect). Using tools, scripts, automation or whatever that assist your testing, go in my understanding under the term that BossBoss Richard Bradshaw coined as “Automation in Testing” or basically as “testing”. This is not part of this article!
In my humble opinion good test automation is providing the following aspects:
Protection of value
Automated tests are implemented to check methods, components, workflows, business processes, etc. for expected outcomes that we see as correct and relevant to our product. This reaches from functionality, design/layout, security, performance or many other quality aspects that we want to protect. We want to protect our product from losing these properties, that’s why we invest in test automation so that we can quickly confirm that the value is still there.
Documentation
Methods or components and especially the code should speak for itself, but sometimes / often it doesn’t. We can’t grasp every aspect that the method should be capable of doing, just by looking at the code. We can help with that, by manifesting abilities of a method or component in automated tests.
Change detection
Test automation is there to detect change. When the code of the product has been touched and an automated test fails, it has detected change. It has detected change of a protected value. This could be a change in functionality, flow, layout, or performance. If that change is okay (desired change) or not (potential issue) has to be decided by a human. Was there a good reason for the change, or do we need to touch the code again to restore the value of our product.
Safety net
The automated tests provide a safety net. You want to be able to change and extend your product, you want to safely refactor code and you want to be sure that you don’t lose existing value. Your automated tests are the safety net to move fast with lower risk. (I don’t say that there is no risk, I just say the risks are reduced with a safety net in place.) Also maintenance routines like upgrading 3rd party dependencies are protected by the safety net. Because changing a small digit in a dependency file can result in severe changes of code that you rely on.
And this is also where the topic of trust comes back to the table. You want to trust your test automation to protect you, at least to a certain degree!, so that you have a good feeling when touching existing code.
What Test Automation is NOT
Test automation is not there to find new bugs in existing and untouched code. Test automation is not there to uncover risks, evaluate the quality of your product, tell you if you have implemented the right thing, or decide if it can be deployed or delivered.
Test automation cannot decide on usability, or proper design or layout. Automation doesn’t do anything by itself. What it does is enable you to re-execute the implemented tests as often as you want, on whatever environment you want it to run, with as many data as you want. As long as you implemented the tests properly (different story!)
Test automation, when done properly and right, is there to tell you, that all aspects, that you have decided to protect, did not change to the degree of protection you invested in.
In the process of creating automated tests, many more things can happen and can be uncovered. I prefer an exploratory approach to test automation, but that is subject to maybe another blog post. But once the test is in place and ready for regular re-execution, it is there to protect whatever you decided is worth to protect.
Test Automation can never be your only testing strategy. Test Automation is one part of a bigger puzzle.
Another blog post on the testing pyramid? RUN!!! But wait, it’s actually about an alternative. Maybe read it first, and then decide to run.
It seems to be an unwritten law that at testing conferences there needs to be at least: – one Jerry Weinberg quote – one mention of the 5-letter acronym in a non-positive context – and there has to be at least one testing pyramid
Whenever test strategies are discussed these days, you will probably also find the testing pyramid been referenced. It is one of the most used models in a testing context that I’m aware of. And yet, my personal opinion is, that too many people don’t understand the actual intention behind it, or are probably unable to properly communicate it. And I blame the model for this!
All models are wrong, but this one isn’t even helpful!
Patrick Prill
The basic testing pyramid (or triangle for some) is mostly mentioned in context with the number of automated test cases to have. A whole lot of unit tests, a bunch of integration tests, and as few as possible end-to-end tests, especially when it comes to this dreadful UI thing. But what does this mean when it comes to actually designing and writing the tests? And this is the point where I feel that the pyramid has some severe short-comings. At least the key aspects that I mean, are often not mentioned together with the pyramid.
Yes, we got it, people misunderstand the pyramid thingy. What’s your solution?
Well, to start with, it’s not my idea. The model that helped me the most for the past four years is Noah Sussman‘s band-pass filter model.
What this model basically states is, that at every testing stage you should find the issues that can be found at that stage. That means, you can probably find the most issues at a unit test level. As a result you will probably have the most test cases here that focus on functional testing.
You don’t want to find basic functionality issues with an end-to-end-UI test!
On a unit test level you won’t be able to catch integration level issues. These stay in the system to be hopefully discovered in that stage. End-to-end-tests should then find the issues when looking at the system all together, when taking a view through the business process band-pass filter.
And this model is easily extendible, by adding new band-pass filters for security testing, accessibility testing, load & performance testing, and so on. All these kind of issues should be found in their respective test stages, however you implement them.
And, of course, the optimal distribution of tests will – in the end – probably for most systems, look like a pyramid / triangle. But it doesn’t have to! It depends on your system’s architecture, design, testability, influence, and so on. Of course, the root cause that some systems cannot reach a testing pyramid lies in some problems of some kind. But that’s not a reason to not having tests!
The band-pass filter model helps you with that. You have to find the issues at the earliest stage possible. At a recent client of mine I was analyzing the unit test set. And basically most tests weren’t unit tests, but integration tests, as most of the methods under test needed DB access. That’s caused by the fact that the MVC approach is not properly used most of the times. But does this project has to refactor all code before properly starting with testing? NO! They have to start then with integration tests, and find ways to filter out all these functionality bugs on the integration test layer, which runs basically after every commit. That’s much better than waiting for the end-to-tests that only come at the end of each release cycle. As a next step they will find better ways to implement their code, with that comes better unit-testable code and more unit tests. Or not, and they stick with their way of mixing MVC and just drastically increase the amount of integration tests. Fine for me!
I gave the developers and testers of my last client an exercise to experience the power of the band-pass filter:
For the next bug report that lands on their desk, they should find a way to reproduce the issue on the earliest possible testing stage. And if that is not the lowest test level (unit test = low, UI test = high, yes, I know, pyramid lingo, duh!), try to check again, if it’s not reproducible one stage earlier.
If they need data, is there a way to simplify the necessary data to reproduce the issue. On an integration level or higher, how do you select or create proper test data? If you made it to the unit test level, you will probably be able to simply define the data or appropriately mock it.
Now that you have a failing test case on the earliest stage possible to find the issue, fix the bug and see the test case turn green. While you are at it, you may add a few more tests on that level. Just to be sure.
Use the power of the band-pass filter model!
PS: The idea to this blog post came during TestBash Home 2021, when the testing pyramid appeared for the first time, in the first talk of the day, and the chat went wild. I stated that I prefer the band-pass filter model and earned a lot of ??? So, here is the explanation of what it is, and why I find it more useful than the pyramid!
Have you ever thought about how much trust testing gets in your projects?
Isn’t it strange that in IT projects you have a group of people that you don’t seem to trust? Developers! I mean, why else would you have one or more dedicated test stages to control and double-check the work of the developers? Developers often don’t even trust their own work, so they add tests to it to validate that the code they write is actually doing what it should.
And of course you trust your developers to do a good job, but you don’t trust your system to remain stable. That’s even better! So you create a product that you don’t trust, except when you have tested it thoroughly. And only then you probably have enough trust to send it to a customer.
Testing is all about trust. We don’t trust our production process without evaluating it every now and then. Let’s take some manufacturing industries, they have many decades, even centuries more of experience than IT. They create processes and tools and machines to produce many pieces of the same kind. Expecting it to have the specified properties and reaching the expected quality aspects every time. Depending on the product, they check every piece (like automobiles) or just rare and random spot checks (for example screws and bolts). They trust their machines – usually to a high degree – to reproduce a product thousands or even millions of times.
We are in IT, we don’t reproduce the same thing over and over again. Can you imagine that for your project you only do some random spot checks, and only check for a handful of criteria each time? If your answer is ‘yes’, then I wonder if you usually test more and why you still test it. If your answer is ‘no’, you belong to what seems to be the standard in IT.
So, what we have established now is, that we don’t overly trust the outcome of our development process. Except when we have some kind of testing in place.
Have you ever realized how much decision makers rely on their trust in test results? If you are a developer, BA, PO, or a tester, who is part of the testing that happens in your delivery process, have you ever felt the trust that is put into your testing? Decision makers rely on your evaluation or the evaluation of the test automation you implemented!
Does your project have automated tests? Do you trust the results of your tests? Always? Do you run them after every check-in, every night, at least before every delivery? Do you double-check the results of your automated tests? When you implement new features, when you refactor existing code, when you change existing functionality, you re-run those tests and let them evaluate if something changed from their expectations. You trust your automated tests to warn you in case something has been messed up. The last code change, a dependency upgrade, a config change, refactoring an old method.
Do you put enough care into your automated tests, that you can really rely on them to do what you want them to do? Why do you have that trust in your tests, but probably not in your production code? And I don’t ask the question “who tests the tests?”
Of course we do some exploratory testing in addition to our test automation. And sure, sometimes this discovers gaps in your test coverage, but most of all exploratory testing is to cover and uncover additional risks, that automation can not supply. So, when you established exploratory testing in some form, alongside your change detection (a.k.a. test automation), you add another layer of trust, or respectively distrust to some parts of your system.
This is not about distrust, we just want to be sure that it works!
In one of my last consulting gigs for QualityMinds, I had an assignment for a small product company, to analyze their unit tests and make suggestions for improvement. The unit test set was barely existent, and many of the tests I checked were rarely doing anything useful. That wasn’t a big problem for the delivery process, as they have a big QA team who is doing lots of (end-to-end) testing before deployment, and even the developers help in the last week before delivery.
Yet they have a big problem. They don’t have enough trust in their tests and test coverage to refactor and modernize their existing code base. So my main message for the developers was to start writing unit tests that they trust. If you have to extend a method, change functionality, refactor it, debug it, fix something in it, you want to have a small test set in place that you can trust! I don’t care about code coverage, path coverage, or whatever metric. The most important metric is, that the developers trust the test set enough to make changes and receive fast feedback for their changes and that they trust that feedback.
I could add more text here about false negatives, false positives, flaky tests, UI tests, and so many more topics that are risks to the trust that we put into our change detectors. There are also risks in this thing that is often referred to as “manual testing”. When it is based on age-old pre-defined test cases, or outdated acceptance criteria. Even when you do exploratory testing and use your brains, what are the oracles that you trust? You don’t want to discuss every tiny bit of the software with your colleagues all the time, if it makes sense or not.
We can only trust our tests, if we design them with the necessary reliability. The next time you design and implement an automated test, think about the trust you put into it. The trust that you hand over to this piece of code. Is it reliable to detect all the changes you want it to detect? When it detects a change, is it helpful? When you don’t change the underlying code and run the test 1000 times, does it always return the same result? Did you see your test fail, when the underlying code changes?
PS:This blog post was inspired by a rejected conference talk proposal that I submitted for TestBash Philly 2017. All that time since then, I wanted to write it up. Now was the time!