Categories
Ramblings

Role of Automation

As Philippa Jennings articulated in her talk at MoTaCon, quality is the goal. Automation is a means. Unfortunately some people do get that muddled. Even worse is mistaking using a tool with quality. Eek!

I love automation for three reasons.

First is unit testing. That super fast feedback when I’m writing code is handy and I love unit tests when it comes to understanding code and PR reviews. I’ve also dabbled with TDD and like that too.

Second is to enable me to do soak or load testing. Clearly I’m not going to be interested in pressing a button over and over 24/7, or manually creating 1,000,000 database entries. I’ll automate setup and execution. These aren’t automated tests as they don’t run automatically with that binary pass/fail but it is a great use of automation in testing.

Finally is the big topic. Automated regression tests. Whether it is E2E or integration, these help test behaviours. Despite many models having E2E as the minority of tests compared to unit or integration, these are the ones that we fuss over the most.

This leads to my point. If I had to manually regression test a feature then I’d be asking about risk to test what was necessary (i.e. not “everything”). The same should apply to automated E2E and to some extent, integration tests. Some testing is still best manual. Exploratory testing offers different insights and can find edge cases that you’d never think of just by staring at Jira or an IDE. Balance is essential.

My advice is to prioritise based upon:

  • Whether you’d bother manually testing that scenario. For me this is:
    • History of defects
    • Areas constantly under change
    • Areas that you’ve just changed (ideally tests written to test a story)
  • Balance of effort to automate Vs run manually:
    • Some tests are expensive to run manually
    • Some tests are expensive to automate.
  • Likelihood of that scenario

So sorta risk x effort x likelihood.

I just want to reiterate that first point. We don’t need to test everything. If we can be adding in automated regression tests to avoid ever doing hands-on regression testing, amazing. But don’t spend hours adding automated tests for the sake of it. On top of that, running the tests aren’t free. The most obvious cost is financial but as with everything we do in computing, there is a financial and environmental cost to running operations.

My absolute ideal is a BDD or ATDD style approach where we’re identifying test cases from the requirements then these tests are written before the story can be closed. As I’ve discussed previously, it is barely an improvement on old fashioned manual QA teams if you’re writing the automated tests in a separate team, weeks later.

Final parting thought. If you are wanting to improve automation for your business, don’t just immediately go to E2E tests. What sort of issues are you looking for? What is the most effective way to catch those issues as early as possible? What is your maintenance budget?

Categories
Experience Reports

Using unit tests to unlock quality (Pt II)

In a previous blog entry I talked about unit testing and how I’ve learnt from my (many) mistakes when writing unit tests and practices that I’ve seen that wind me up.

Today I’d like to talk about how I’ve been writing unit tests recently, employing the ideas of TDD (test driven development), and some of the pros and cons of using this approach.

When I first learnt of TDD and was strongly encouraged to use it, I thought it was about writing tests then code. This is kind of true but it also a gross simplification and one that others that I’ve spoken with also have. At the time I really didn’t like it and rejected the idea but having learnt more, I think it is actually kind of swell.

TDD is more iterative and helps you design the code.

  1. Write a “single” unit test describing an aspect of the program
  2. Run the test, which should fail because the program lacks that feature
  3. Write “just enough” code, the simplest possible, to make the test pass
  4. “Refactor” the code until it conforms to the simplicity criteria
  5. Repeat, “accumulating” unit tests over time

Here’s a basic example of TDD for a method to take two strings and adds them:

  1. Start with the most basic case:
    • Assert.Eq(myThing.Add(“1”, “2”), 3)
  2. Write code to make that pass.
  3. Tidy up the code you’ve written
  4. Repeat the process as you build up functionality
  5. What’s next? Error handling with string parsing:
    • Assert.Null(myThing.Add(“cat”, “2”))
  6. After writing the test, see the result and fix if necessary (seems likely at this point).
  7. Okay, time to do the tricky bit. Again, write a new test, see the result and iterate:
    • Assert.Eq(myThing.Add(“one”, “2”), 3)
  8. Some edge cases:
    • Assert.Eq(myThing.Add(“-3”, “four”)
    • or: Assert.Eq(myThing.Add(input1, input2), expectedOutput)
  9. What’s next? Error handling:
    • StrToInt.returns(null) / StrToInt.Throws(ex)
  10. And so on…

One thing I quite liked was when I’m testing my interface for the new class within other classes. It had me thinking “how do I want to handle these situations?”. Previously I would have written a wad of code, handling errors as I see potential to bump into them etc then knowing what I intended the code to do, I’d write the test to ensure it passed. TDD got me more focused on desirable behaviour.

The other benefit that I found was that if I found adding an extra bit of functionality required touching other unit tests that weren’t interested in that change, I knew that my code design was wrong. I was building much more independent tests and therefore, I hope, more maintainable code. If we decide to change how to handle one bit of a method, I won’t be having to update every sodding test like we’d done in the past.

Of course the benefit of better and more maintainable code could just be because I’m more experienced (even if I barely write code since returning to a test role). However I remember feeling especially chuffed with the code.

I’ve heard that TDD can help reduce manual testing required. Personally I’m not sure if that is the case for me given that historically I’ve had very good coverage – even they were written in an overly complicated manner. Anyway, I’d be very apprehensive about reducing the functional testing on the basis of code being unit testing. However I was at least happier than I wouldn’t need to repeat manual dev testing.

There are of course drawbacks. I would have a torrid time if I tried doing this in an area that has really badly written code and tests. It was definitely easier to embrace when I was adding new features.

Also thinking back to some of my previous projects, I may have started work on a changeset with a less defined idea of what I wanted to do. We all know (hopefully) of exploratory testing but I’ve often embraced “exploratory coding”, where I’m exploring ideas of how to put together a class or how an API works through the code.

You can probably still use TDD with this early doors by using behaviour driven tests with little thought on implementation. However my problem here is that if I’m not confident on how something will work, I can find myself adding/removing parameters and changing my design of the code quite a bit until I get a “feel” for it.

I’ve found that if it isn’t a clear area that I’m working on, I might do my exploration of the code, see how it works, understand what I want to be doing and importantly, know that my code is like my exploration notes and not get attached. Then when I have an understanding, I’ll switch to TDD and write it “for real”. However I’ve only limited experience of doing this so I’m not sure how practical it is.

Finally in my experience so far I’ve found that it was definitely slower than some of my similar sized user stories in the past. In the short term it may negatively impact velocity and leave a bad impression but if you’re writing tests that are easier to maintain then this should benefit you in the long run.

Yes, it took me longer to write each changeset, but I wasn’t re-writing unit tests every time my next changeset built upon my previous code. The next time I work on this feature I expect to be quicker than I would have in the past.

In the long run, TDD seems like it will not only help me write better code and tests but whoever picks up working on that area will hopefully thank me for the effort. I’d certainly be grateful if the previous developer in an area has written maintainable and testable code.

Categories
Experience Reports

Using unit tests to unlock quality (Pt I)

When I started working as a developer my mentor taught me to write unit tests with each changeset, so I did. After switching team, my new lead & mentor had us doing the same and I learnt new techniques to write more complex unit tests. When a couple of newer members joined the team, getting unit tests written was something I pushed hard. After all, it was good practice that all good software engineers do.

One of my strengths, or so I thought, was writing unit tests for any and every method. No matter how ugly to code that the test was for was, as a (very small) team we had great coverage… even if it became a running joke that maintaining the tests was often most of a user story.

In hindsight I realise that I was wrong on two accounts.

Not all developers are writing unit tests anywhere to the level that I thought.

It surprised me when we kept having regressions in sections of code. I asked why unit tests weren’t catching them. The simple answer was the code was too hard to unit test.

Now in the developer’s defence here, this is a very old code base that they were building upon and there was no existing coverage but I want to talk about the idea what code can be too hard or “not possible”.

One of the most common challenges that I’ve seen is with calling APIs (Windows / first or 3rd party) or where your method relies on an external entity. Some examples might be using DirectX, accessing the file system or calling an API for a third party system.

The solution is, in theory, pretty simple. Mocking. Rather than calling DirectX directly, have a wrapper and call that. Keep your logic separate from the API calls and you can test it. This is good for developing maintainable code as well as good for your testing. There may be the odd exception where your wrapper might complicate things too much, but that should be a rarity not a norm.

The other reason for not using testing is where timing issue make the tests flakey. Now this is a good reason to not automate something as I believe that a flakey test is worse than no test. However again in most cases I have found the mocking is again the solution. In projects where I’ve been a developer we always have wrappers for our timers so that if we want to test the behaviour in response a timer elapsing, we just invoke the timer.

I’ve found dependency injection to be really useful in making my code testable. We’ve also used reflection as well where you can insert your mock into a created object. You can also set certain properties so that if you’ve got a private member for “isAlive” then you can test “personUnderTest.PokeWith(stick)” with different values for “isAlive”, without having to include steps like “personUnderTest.ThrowOffBridge()” in your setup (meaning changes to ThrowOffBridge can affect PokeWith).

Another thing that I’ve found a little unsettling is “it’s all pushed, I’ve just some unit tests to write.”

No, no no.

There’s a few big issues here:

  • It assumes that your code would pass unit testing before trying.
  • It assumes that your code is testable.
  • If either of those are not true then you will have to re-write the functional code, dev test it again then get it through review again.
  • It can lead you to write unit tests to pass, rather than to test.

My other learning is how bad my tests and code were.

Some of the methods that we wrote were massive and complicated. This meant that in order to unit test one part of the code, I needed to mock and setup absolutely loads of other code. The worst part was making changes. Because we decided that one small part of the business logic needed changing, I was fixing up dozens of unit tests. It was nasty.

I really have learnt the value in keeping things small and ensuring that your methods are serving one function, not “go do everything”.
Some words on how keeping things small is better.

The other major mistake that I made was being what I thought was clever in creating tests that I could set a bunch of inputs on different parameters then the expected output. For example changing how some of my mocks would be setup based on logic in my unit test. Only needing one unit test to cover a bunch of different business logic is genius right?

No. No it is not. It meant that I had tests that were very hard to debug when they failed. It also made it really awkward when we made a tweak or extension to the behaviour.

Lesson learnt: Keep your code and tests simple!

In my next post I will explore more on the technique(s) that I’ve been using to improve my unit tests.

Categories
Ramblings

Automation Test Engineers re-enforcing 2 tier engineers

Before I begin, I have spent several years as a software engineer and was decent enough at it. As part of this I would write my own automated tests. Since switching to test, I’ve developed a host of handy test tools, developed simulators and even made my own automation tool that used our SDKs to test stability through a huge range if activities.

My point is, this is coming from someone who has experience of automation, even if I consider myself as a manual tester.

Anyway, the point…

The job market in my city is predominantly junior test engineers or senior automation test engineers. Companies are desperate to hire people who can write and execute automated tests. I would like to ask these companies, why get a dedicated person in to do this?

It might seem a little wild, but why do you need to hire someone for this role? Are these companies not writing automated tests? Or are the developers writing them?

You can probably see where I’m going here. Developers are more than capable of writing automated tests and when surely if a company is trying to follow good working practices like scrum, LeSS, ATTD, BDD, TDD and buzz word driven development, then surely the developers are writing the automated tests as part of the DoD for a PBI/story to move to dev done?

Having now made the case for automated tests to be in the ownership of developers, I now want to talk about why being an automation test engineer is regressive.

There has long been the concern or battle as to whether test engineers are second class engineers. I’m not entirely sure that picking up bits of work that software engineers often dislike or see as beneath them is helping to further the value of dedicated testers.

I’ve definitely felt like my skills and role as a test engineer has been most valued when embedded within the feature team, mostly picking up stories in dev done & awaiting testing. However seeing people taking up roles where they act as the safety net in a separate test group where work is lobbed over the partition kind of saddens me.

People who have invested lots of time, effort and maybe even money into learning automation may be scoffing at me right now. I’m not saying it is wasted effort. Far from it. If you enjoy it, rather than being an automation engineer, what about just being an engineer picking up any PBI like the rest of the team?

If you do love your testing and want to keep testing, like myself, there is plenty work to do. Use programming skills that you’ve learnt to automate some of your tests. For example creating a script to further load the system or maybe to help parse results from log files. How about pairing with a developer so they write the functionality whilst you write tests.

Even without the coding, a tester’s skillset is still massively valuable. Get yourself involved in backlog refinement. Go larvae hunting. Coach your team. Get involved in security. Help your team shift left.

Going forward, rather than replacing a team of manual testers with a team of automation testers, let’s use our skillset to identify risks, bugs and possible UX concerns as early as possible.