This week I finally experienced what it’s like to turn testing philosophy into practice and then watch it collide with real code. After a long stretch of trial and error with the QR-scanner tests, I was able to step back and see the bigger picture.

Mocking Strategy

Mocking started out feeling repetitive, so many when calls! But repetition revealed patterns. I created reusable helpers and fixtures, and learned to mock only what truly matters. For example, JWT tests don’t need a full database simulation, just a valid secret key. Mocking at the system’s boundaries (like the CouchDB client) keeps tests focused and easier to maintain.

  • Helpers and fixtures cut down on repetitive stubbing.
  • Realistic JSON responses and deliberate exceptions covered happy paths and error states.
  • One well-written MockCdbClient was enough, over-mocking just adds noise.

Clear Layer Boundaries

Another big realization came from drawing clearer lines between unit, widget, and integration tests. Unit tests are perfect for small pieces of logic, while widget tests focus on a single widget in controlled conditions. Integration tests, on the other hand, cover the full user journey. Separating these layers kept me from duplicating work and helped me see where each type of test adds value. Instead of trying to cram everything into one giant integration test, I can let each layer handle its own responsibilities and trust that they fit together.

Async & Widget Lifecycle

Flutter’s async model was another learning curve. Understanding when to call pump, pumpAndSettle, and how frames and dialogs interact was crucial. Without the right timing, tests would pass sometimes and fail at others. By carefully awaiting animations, dialogs, and service calls, I learned to keep the UI in sync with test assertions. This gave me a clearer mental model of how Flutter actually builds and updates widgets under the hood.

Type Safety in Tests

A seemingly small change, switching from a plain string to a UuidValue, broke a lot of my early mocks. At first it felt like a setback, but it reinforced how important strong typing is. Mocks have to match exact types, and consistent test data prevents subtle bugs. It was a good reminder that tests are only as reliable as the types and data they use.

Reflections & Insights

  • Isolation keeps tests reliable and fast.
  • Boundaries matter: unit vs. integration tests need different approaches.
  • Arrange–Act–Assert isn’t just a slogan; it keeps tests clear and intentional.

What’s Coming Up

Next week I’ll wait for feedback and start pruning any test cases that turn out to be overkill. The changes are already up for review, so I’m curious to see what comments come back. Whether it’s approval or a list of revisions, the learning will continue. At the same time, I’ll begin the next task: enabling real-time UI updates by reacting to database change events instead of relying on manual refresh.


0 Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.