BDD with Flutter the easy way

BDD with Flutter the easy way

Leveraging test groups to achieve BDD Styled Unit Testing (No third party libraries necessary)


If you’re like me, and enjoy TDD but like to approach it from a BDD style, then this blod post might be able to help you. Firstly, for people who aren’t quite sure, lets look at the difference between TDD & BDD.

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only. This is opposed to software development that allows software to be added that isn’t proven to meet requirements.

John Callaway – “A Pragmatic Approach to TDD”

The process of TDD is clear

  • Write tests first
  • Watch tests fail
  • Implement just enough code to pass the test
  • Watch tests pass
  • Refactor implementation
  • Repeat.

Simple process that leads you to the solution through the quickest possible route.

BDD is a process designed to aid the management and the delivery of software development projects by improving communication between engineers and business professionals. In so doing, BDD ensures all development projects remain focused on delivering what the business actually needs while meeting all requirements of the user.

Konstantin Kudryashov, Alistair Stead, Dan North – The Beginner’s Guide to BDD

A little more tricky of an explanation, but quite clear. As TDD is aimed at writing tests based on functionality, BDD is a little more high level with the main focus being on user requirements.

Let’s look at some code…

It’s fairly simple to achieve something that looks like BDD. By creating meaningful test names, we can output tests that read out each behaviour we are trying to test. Here’s an example.

This works! Great, but here’s the test suite output.

Urgh, this is almost helpful, but here’s the thing. This is one class out of hundreds/thousands! This is in a small/medium project, and after a while, things just get messy. My current project is no where near production ready and is already hitting the 1000 unit tests mark. Can you imagine trying to understand if every behaviour has been successfully tested, or even worse, imagine trying to search for a specific behaviour in all that white noise that is being output. What if there was a better solution?

Luckily there is! I can already see the comments to this post in my head pointing out that there the tests get a little messier, but in all honesty, I don’t care. I think this solution that I am about to provide has helped me a great deal in understanding the behaviour of my project.

Groups to the rescue!

As mentioned in my subtitle, there is no third party tools required for this, you simple need the basic flutter_test or test suit to achieve the end goal.

Let’s look at the example class I am using for this example. It’s an AuthManager class (Yes, Uncle Bob wouldn’t agree with this naming convention, Noun > Verb). The class in question has the following responsibilities:-

  • Has reference to the single logged in user (Source of truth)
  • Has public facing functions that allow you to create/login/register/logout
  • Is not a singleton but is a single instance in our application.

Not too much going on really, quite a common example.

The way I see this logic, it nicely splits into two sections:-

  • User is logged in state
  • User is logged out state

Using these states I can create my first behaviour GIVEN state.

  • GIVEN a user is logged in
  • GIVEN a user is logged out

To allow our code to break out output into sections, we can simply use the following

group("GIVEN a user is logged in") { }

Simply put, this is a wrapper class that will break down out outputs. The great thing about this is we can user as many groups as we like! Based on that I am now going to do this again, to start testing out WHEN logic. First up, check to see if a user is already logged in.

group("GIVEN a user is logged in") { 
    group("WHEN isUserLoggedIn is called") {

Okay so is now where I expect people to argue a little, but it’s fine. Take what you need from this post. If we only have a single THEN statement, do we need a second group? Technically no, the second group could be WHEN & THEN in one statement, but as a personal preference, I want them grouped. Now we have our GIVEN & WHEN, let’s add out tests for THEN.

group("GIVEN a user is logged in") { 
    group("WHEN isUserLoggedIn is called") {
        test('THEN true returned', () {
              // Given

              // When
                final isUserLoggedIn = authManager.isUserLoggedIn();

                // Then
                expect(isUserLoggedIn, true);

Boom! That’s it. Greatness achieved. Again, this is up to peoples interpretation, but in my honest opinion this has helped me greatly split my tests into logical behaviours. Again, this has been approached from a TDD manner, covers all unit testing, describes all our behaviours and is extremely easy to manage! Here is my output.

Here is a little more code to look at. Not too messy, although, the nesting is a little annoying but I genuinely believe it’s worth it!

Please let me know if you have any ideas on how this can be improved! Until then, happy coding.

Phill Wiggins

Phill Wiggins

💻 Mobile Apps Developer with experience in #Android, #Flutter and more. Tech geek and blogger, Gym rat & #Trance lover! 👔 Employment: Senior Mobile Software Engineer (Permanent) 🎂 Birthday: 15th May

2 thoughts on “BDD with Flutter the easy way

    1. If it’s possible to avoid adding third party libraries and achieve the same goal, then even better. Granted Gherkin is a powerful library, but has a learning curve and adds extra maintainability steps. Between the above and the Robot Testing Framework, I really don’t think it’s necessary to make testing any harder. Simplicity is key with this. I feel the same way about most BDD libraries I’ve used before, Cucumber/Gherkin mainly.

Leave a Reply

Your email address will not be published. Required fields are marked *