logo logo

My Recent Obsession with Hermetic Tests

My Recent Obsession with Hermetic Tests

Over the years, after dealing with test flakiness due to the dependencies surrounding them, I started searching for answers. One day, my mentor sent me an article that discussed hermetic tests. It was the first time I heard the term, but the concept seemed very intriguing, so I started researching it 📖

What are hermetic tests?

A hermetic test is a test that is completely self-sufficient. It is fully independent, therefore every time a test is run, you are absolutely sure that a failure is a real failure, and had nothing to do with a dependency going wrong.

The way that it works is you set up a test by injecting mock data or completely mocking your dependency. While this looked like a perfect test for the problems we’ve been encountering with flaky tests, the way to achieve it meant a fundamental change to the way the software was developed.

Some context

We decided to implement it for the mobile app that we were working on 📲 This time, we wrote our mobile app tests on Espresso for Android, and XCUI tests for iOS. They share similar patterns and architectures from the testing point of view. For this article, we will discuss the android UI tests and how we managed to achieve a hermetic tests pattern.

The architecture of mobile apps, in general, meant that there were two levels of software dependencies. The first was the API it was talking to, and the second was SDK dependencies. So we will see how we managed to mock these two dependencies.

Android case study

The recent app that we were working on, used the Hilt Android library in its architecture. Hilt is primarily used for dependency injection. It also used a retrofit library for making network calls.

Core app

UI Tests Architecture

Mocking the API

This was the easiest part since for both mobile and web apps there were so many libraries that helped mock network calls. We set up test data and left it as part of the UI test package, and let the MockWebServer library mock them.

private val server: MockWebServer = MockWebServer() 
private val MOCK_WEBSERVER_PORT = 8000

@Before
fun init() {
    server.start(MOCK_WEBSERVER_PORT) 
    server.apply {
        enqueue(
            MockResponse()
            .setBody(MockResponseFileReader("jsonplaceholder_success.json")
            .content))
        }
    }
}

From the above example, we can see how we are able to mock an API call using a mock test data file 📂

Now that we are over the easy bit, let us look at the second part where we try to mock the SDK dependencies. This is where it gets interesting in a good way. In my own experience, forcing hermetic testability has made it mandatory to use the best practices while software development. The reason being, it fundamentally forces the development to be modular and have independent components that can easily be mocked or faked.

With the android hilt architecture, the way we implemented mock dependencies was using the @InstallIn annotation:

@UninstallModules(AnalyticsModule::class)
@HiltAndroidTest
class SettingsActivityTest {

    @Module
    @InstallIn(SingletonComponent::class)
    abstract class TestModule {

        @Singleton
        @Binds
        abstract fun bindAnalyticsService(
            fakeAnalyticsService: FakeAnalyticsService
        ): AnalyticsService
    }
}

In the above example, you can see that the actual implementation of the analytics module has been removed using the annotation @UninstallModules. Then the mock implementation was injected into the UI tests using the @InstallIn annotation.

The result

Now that we have mocked the two main dependencies before even the test runs, the app opens in the exact state that you want it. So any test you run fully tests the core logic of the implementation rather than the complete app along with the dependencies.

Counter argument

There is a really valid argument around not following hermetic tests pattern but testing the dependencies, too. I agree with where they are coming from, but when you get to a point where having UI tests don’t add value because of the flakiness that the dependencies produce, I feel this test pattern certainly adds so much value.

On top of that, there are scenarios where there is user input required due to the nature of the workflows (eg. hardware dependencies). Using a hermetic test pattern there would definitely help you test the complete core workflows, including failure scenarios, by implementing hermetic tests.

Was it worth it? 💯 

Conclusion

The example that has been provided in this article is for mobile apps. Although, the hermetic test pattern can work really well for any UI test automation framework. For example, with web apps, you can consider the hermetic test pattern as an extension of the storybook for react.

As long as you can inject your API and state into the UI and construct tests around it, you will get a lot of value out of it and have tests that run super fast, with less flakiness ✅ I highly recommend giving this a try with your existing UI test automation frameworks. Good luck!

Avatar

About the author

Jaswanth Manigundan

I lead, train and mentor a team of passionate testers at Transpire.

I’m a developer turned tester. Even though writing code to build applications is so much fun, my now best friend showed me a few years ago that helping write good quality apps is much more fun. I jumped into testing from being a developer and have never looked back. I’m very passionate about native mobile and web testing frameworks, especially Espresso (Android), XCUI testing (iOS), Detox (React Native) and Playwright (Web).

CI/CD for mobile is something I have been working for the last few years. On the web side of things, AWS CDK has been quite intriguing. I have been an advocate of Infra as Code since working on it. Devops is something that is quite exciting for me and always evolving.

I have been speaking at a few international conferences on Native mobile and web app testing and CI/CD for mobile and web apps. Saucecon’21, Ministry of Testing’s Testbash and UI testing week events to name a few.

Feel free to chat anything testing :)

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Leave a Reply

popup image

A new world for test automation

Join 150,000 testing & dev teams taking their web & mobile testing to new heights, using #1 FREE test automation platform, designed to help deliver quality at speed.
Get Started
FacebookLinkedInTwitterEmail