Archive for November, 2022

TDD tips for scrum teams

“Any tips for getting your Scrum team on board with practicing TDD as a team?”

Yes!

  • It starts with the individual
  • Lunch and Learns
  • Try it with one small story
  • Code Review
  • Ping-Pong Pair Programming
  • Divide and Conquer
  • Book Clubs

Expanding each of those points…

It starts with the individual

A few common situations that prevent teams from adopting TDD include:

  • Can’t make time in current project
  • Legacy system that makes it very hard to practice TDD
  • Team members not willing to try it
  • Lack of support from the business

Those situations should NOT prevent an individual to do it.

Practice TDD on your own time so you can build your skills.

If others see you do it, they may join you. If they do, great. If they don’t, you are still growing.

Lunch and Learn

If you decide to take it on your own, offer lunch and learn sessions for your team to share your experiences, struggles, successes.

The purpose is not to wait until you become an expert; share it as you learn.

Make it a recurring meeting.

Write down ahead of time the things you’d like to share (including how and why you failed, and how you’ve overcome it – or not!).

People aren’t showing up or look uninterested? Consider putting it out as blog posts. Why? There are always people out there who will relate to your struggles and successes.

Try it with one small story

Pick one small story, or a small piece of a story, and commit to doing TDD.

You may fail (many times). You may succeed. Either way, share your findings with the team:

  • was it taking too long?
  • Why?
  • Lack of knowledge? Practice?
  • Difficulties with the legacy code?
  • What kind of difficulties?
  • Too many dependencies?

Work as a team to figure out how the hurdles could be overcome.
Your weaknesses might be someone else’s strengths. And vice-versa.

Share the experience at the sprint retrospective. Figure out the next step and commit to it.

Code Review

When doing code review, start by reviewing tests/specs.

When writing tests first, consider asking for a code review before implementing it, to make sure you have a good understanding of the problem that needs solving.

During that review, share any difficulties you see. The reviewer might know how to help you. And if not, you may find a clear path ahead of you just by articulating your thoughts and sharing it with someone else.

After done with the implementation, ask for another code review. This time, maybe share how you’ve addressed the difficulties. Also, maybe discuss ways how the test code could be improved.

Ping-Pong Pair Programming

Consider ping-pong pair programming: one person writes a test, the other one writes the implementation. Then swap.

Set a time-box. Do not let interruptions get in the way.

Let others know what the pair is up to so they can help avoid interruptions during the time-box.

Share the lessons learned with the team.

Divide and Conquer

Work as a team.

Divide the challenges so that each team member can focus on learning one thing, and then share the findings with the team.

Here are some ideas on what to learn:

  • Test frameworks for the tech stack
  • Testing legacy code
  • Tools such as Cypress.io, Cucumber, SpecFlow, Selenium, etc.
  • How to test code that makes heavy use of libraries or frameworks such as Angular, React, Mass Transit, etc.
  • How to write better specifications in Given-When-Then

Book Clubs

Run book clubs!

Build knowledge and skill together as a team.

Choose a book that seems to fit the team’s current skills, set a cadence (maybe once a week during lunch breaks?), start reading, and discuss the findings together.

Here are a few books you may want to consider: Recommended Reading on Testing

In Summary

I have used all of these techniques. Still do.

I pick and choose whichever one works better depending on my current situation. Sometimes the one I pick doesn’t work on a given team. I drop it, and try another one.

Whether the team thinks of TDD as Test Driven Development or Design and whether they use the terms test or spec, that depends on the team’s maturity. Different people, different backgrounds, different ways to learn.

It all starts with one person. Do not wait for that person. Be that person.

Advertisement

,

Leave a comment

Is it a code smell to use Mocks in unit testing?

Maybe.

But first, I’ll start by clarifying that developers asking that question usually mean “test doubles“, instead of “mocks”.

Some situations that might indicate code smell in tests include:

  • using several test doubles to test one thing
  • complex test doubles setup

Both cases aren’t only an indication of a code smell in the test; they often indicate a bigger problem: code smell in the design!

If the system under test (SUT) requires too many test doubles to satisfy its dependencies, it likely violates the Interface Segregation Principle

A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use.

Take the example below:

public class SomeClass(Dependency1 Dependency1,   
Dependency2 Dependency2, Dependency3 Dependency3, 
Dependency4 Dependency4)
{ 
 public void SomeMethod()  
 {    
   if (dependency1.CheckThis() && 
       dependency2.CheckThat() && 
       dependency3.CheckSomethingElse() &&   
       dependency4.FinalCheck())    
   {
       // do the thing...    
   }
  }
}

 

To write a unit test for SomeMethod, we would need mock each one of the 4 dependencies.

By inspecting how those dependencies are used, we could identify a new abstraction that offers what SomeClass needs:

public class SomeClass(Dependency Dependency)
{  
  public void SomeMethod()  
  {   
   if (dependency.CanIDoTheThing())     
   {     
     // do the thing...    
   }  
  }
}

 

Now there’s only one dependency to deal with in tests, and it’s easier to learn what that dependency provides.

An example of code smell

Here’s a test I’ve run into many years ago:

According to the name of the test, it verifies that the “PUT method returns OK and PortfolioModel when service response is successful“.

When I’ve read through the test, these considerations came to mind:

  • Number of stubs and mocks (mockPortfolioService, portModel, response, portfolioModel)
  • Overuse of Arg.Any
  • Arg.Any hiding the meaning/intent of several parameters
  • Unclear what “Configure” is all about (what does it mean to return true from it?)
  • What’s the difference between portModel and portfolioModel? Why are both needed?
  • The file had about 40 tests that looked very similar to this one in terms of mocks and stubs; a product of copy-and-paste.

After raising the issues with the developers on the team, we identified the design issues that resulted in tests having to be written that way. The tests were rewritten to isolate and call out the issue, and a design change was proposed.

Leave a comment

Is it worth writing test code for application logic?

“Is it worth writing test code for application logic (as opposed to business logic)?”

  • Yes.
  • Not all of it.
  • Not always.

Test what yields business value.
Making the development effort more efficient may yield business value.

If application logic is directly related to business value, it needs automated tests.

If lack of tests for application logic delays development efforts (including manual testing), then it’s worth writing tests.

An example…

As a developer, I like being able to take an API contract designed by the team (the URI to the endpoint and the shape of the input and output) and write a quick test for it that we can use to make sure the endpoint works as expected.

This is what such tests look like:

On the left, we see the test. On the right, we see the expected payload and response.

This integration test verifies that:

  • The route exists
  • The json payload can be handled
  • The response gets serialized into the expected json

But not only that, it also verifies any middleware that exists between the route and the controller, so things like authorization, model binders, dependency registrations, etc.

We either find a test harness, or build one, to make such tests easy to write, so there’s no reason not to.

The example above:

  • Does not need any special tool
  • Is written in plain C# and xUnit.net

In summary, when deciding what we should write tests for, “application logic” also comes into consideration.

Leave a comment

Is TDD something you do sometimes or all the time?

That’s another common question: Is TDD something you do sometimes or all the time?

The short answer is neither. Or, “it depends”.

But let’s explore the long answer…

When I started learning TDD, yes, I’d do it all the time.

“But have you always worked on projects where you could do TDD all the time?!”

Most certainly I have NOT!

There are times when I can’t do TDD on a project.
That doesn’t prevent me from still doing it on the side.
I learn and practice it on my own time.

TDD became something I do most of the time, but never all the time.

So when do I NOT do TDD?

There are situations when I specifically choose not to do TDD. Here are some that come to mind…

Exploring a library or framework

When trying to learn what a library or framework can do for me, and how I’m supposed to use it.

Once I identify ways I’ll use it, I often refactor those tests into Given-When-Then, documenting my understanding and assumptions, so I can later remember what parts of it I’m using.

That approach also provides a safety net when consuming updates to those packages (identifying breaking changes and such).

Exploring approaches

Sometimes I need to implement features that currently lack clarity, so I want to gather feedback from stakeholders as soon as possible. I may try a few different approaches and won’t do TDD.

I will, however, use BDD (Behavior Driven Development) to describe, to the best of my ability, the feature we’re building.

Solving small problems

If a problem is too small and yields very little value, I may skip TDD.

Pitfalls of TDD

Remember Design Patternitis? That’s something most developers face when we learn Design Patterns; we start trying to apply them everywhere! As mentioned earlier, TDD is not something to be done always, everywhere, every time.

Another situation I see often is “copy-and-paste inheritance”; tests are initially written carefully following TDD, but then every new test comes from copy-and-paste, without any effort going into refactoring the test code. This is a pitfall that happens in most automated tests, and tests written following TDD can also suffer from it.

But BDD…

I try to do BDD most of the time, even if it means writing the stories and scenarios on a napkin.

Many times I don’t even know how I’ll actually implement the specs/tests, but I still write those English sentences before trying to write any code. No GWT, No Code!

Leave a comment