Better Test Dependencies: Exhaustivity

Episode #138 • Mar 15, 2021 • Subscriber-Only

We talk about dependencies a lot on Point-Free, but we’ve never done a deep dive on how to tune them for testing. It’s time to do just that, by first showing how a test can exhaustively describe its dependencies, which comes with some incredible benefits.

Exhaustivity
Introduction
00:05
Testing features with dependencies
01:44
Unimplemented dependencies
11:16
Adding analytics to features
20:12
Testing analytics exhaustively
30:45
Next time: failability
40:55

Unlock This Episode

Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.

Introduction

We have talked about dependencies on Point-Free a bunch. Some of first episodes were about the concept of “dependency injection made easy.” In those episodes we claimed that a lot of the complexities of dependencies can be alleviated by embracing structs instead of protocols and by coalescing all dependencies into a single mutable value.

We took these ideas further by showing how dependency injection can be made composable and modular with the Composable Architecture. The notion of dependencies was baked directly into the core unit that powers a feature, known as a reducer, and from that we got all types of benefits, such as isolating our features into their own modules, sharing dependencies between isolated features, and even using different versions of a dependency for the same screen depending on the context it was presented. There was a lot of cool stuff we explored in those episodes.

And then most recently we kicked things up a notch again when we did a deep dive into how to design dependencies. That is, how do we allow 3rd party code run in our applications without it running amok. The more 3rd party code you drop into your code base the more chances there are to make your code untestable, strain the build systems, and accidentally break things like SwiftUI previews or even the ability to run in the simulator.

We want to continue this theme a bit more because there’s still something we haven’t spent a ton of time on, and that’s how to best wield our dependencies in tests. One of the main selling points of controlling dependencies is that it allows us to easily write tests for our features. But the power of those tests largely depends on we make use of the test dependencies, and there are some really cool tricks we can employ to increase the value of our tests.

This episode is for subscribers only.

Subscribe to Point-Free

Access this episode, plus all past and future episodes when you become a subscriber.

See plans and pricing

Already a subscriber? Log in

Exercises

  1. Define an assertion helper that asserts against the events that buffer into the events array and clear them out to prepare for the next assertion.

    Bonus points if you make it generic over any collection of equatable elements so that it can be extracted into a reusable library!

    Solution

    Because events has been extracted to a test case property, the simplest solution can be defined as a helper method:

    class TodosTests: XCTestCase {
      …
    
      func assertTracked(events: [AnalyticsClient.Event]) {
        XCTAssertEqual(events, self.events)
        self.events.removeAll()
      }
    }
    

References

Designing Dependencies

Brandon Williams & Stephen Celis • Monday Jul 27, 2020

We develop the idea of dependencies from the ground up in this collection of episodes:

Let’s take a moment to properly define what a dependency is and understand why they add so much complexity to our code. We will begin building a moderately complex application with three dependencies, and see how it complicates development, and what we can do about it.

A Tour of the Composable Architecture

Brandon Williams & Stephen Celis • Monday May 4, 2020

When we open sourced the Composable Architecture we released a 4-part series of episodes to show how to build a moderately complex application from scratch with it. We covered state management, complex effects, testing and more.

Composable Architecture: Dependency Management

Brandon Williams & Stephen Celis • Monday Feb 17, 2020

We made dependencies a first class concern of the Composable Architecture by baking the notion of dependencies directly into the definition of its atomic unit: the reducer.

Composable Architecture

Brandon Williams & Stephen Celis • Monday May 4, 2020

The Composable Architecture is a library for building applications in a consistent and understandable way, with composition, testing and ergonomics in mind.