ZIO Test: What, Why, and How?

Functional World meetup is a global event aimed at improving learning and knowledge sharing among developers with a passion for functional programming. This year’s edition was focused on the ZIO Scala library. In that vein, we invited developer and ZIO enthusiast Marcin Krykowski to speak about the functional power of ZIO Tests.

Marcin previously worked as a developer at Scalac. He is a poet at heart and believes that Scala and ZIO enable poetry through code. In addition, he recognizes the immense value of approaching software development problems from a beginner’s perspective. 

With that philosophy in mind, Marcin delivered an entry-level talk that’s accessible even to someone who has never encountered the ZIO Test framework before. 

His presentation is an “ultimate guide” to the ZIO Test framework. Marcin wanted to ensure that after finishing his talk, watchers would be ready for their journey with ZIO Test, meaning they would be able to import a project and run some initial tests. 

Answering Common Questions About ZIO Test

Marcin covered many aspects of the ZIO test framework and answered several important questions:

  • How do I start with ZIO Test?
  • How do I write my first tests?
  • How can I improve tests with ZIOTest?
  • How do I use test effects with ZIO Test?
  • How can I make tests more readable?
  • How will my codebase benefit from ZIO Test?

With all that in mind, let’s dive into Macin’s presentation. He began by exploring some of the benefits of the ZIO Test compared to other frameworks. 

What Makes ZIO Test Better Than Other Frameworks?

You probably already have a set of preferred testing frameworks for use with your projects. So why should you invest time and effort in familiarizing yourself with a new one?

Marcin believes that employing ZIO Test is worthwhile because many frameworks suffer from the following problems:

  • Leaking resources: Some frameworks cause locking, which prevents other components from using a shared resource. A test may have acquired a resource and returned a result, but the resource may still be locked. 
  • Futures: Some test frameworks use futures as the main test effects. 
  • Multiple versions and platforms: If you’re coding using Scala, you shouldn’t use it as your main effect. This is because you likely work with various versions and platforms, such as Scala and ScalaJS. Rewriting specific tests for each platform wastes time. Instead, tests should be portable and work with more than one version.  
  • Dependencies on other services: You may have to fetch code from different parts of the project to make tests work. 
  • Concurrency: ZIO Test allows multiple tasks to run at the same time. 

All of the issues described above, while not insurmountable, slow down the testing process. This costs time and money and affects client delivery. On the other hand, ZIO Test offers a range of functionalities that remedy these problems:

  • Functional effect systems
  • Referential transparency
  • Resource safety
  • Environment type
  • Interruptibility

After outlining the benefits of the ZIO Test, Marcin delved into the specifics of setting up and running tests. 

How to Set Up a ZIO Test

To set up a ZIO test, first import the library dependency, as seen in the example below.

zio test
How to import ZIO Test 

Marcin primarily uses the SBT version of ZIO Test. Originally known as “Simple Build Tool” and now shortened to its initials, SBT is a build tool specifically for Scala and Java. 

zio test
Code for a simple test using ZIO Test

Once ZIO Test has been imported, it’s ready to use. The image above shows an example of a typical first test.

As a slight disclaimer, Marcin pointed out that he created the example above as a rough guide. It doesn’t include specific requirements like environment or error type. And different ZIO versions don’t always function in the same way. 

How to Write a Test

To write a test, begin by defining the suite that contains other tests. After doing this, make the assertion. In the example below, the assertion tests if the result equals the string “Hello, this is Paul.”

zio test
An example of a test as an effect

Some testing frameworks use futures as effects. Futures are values that stand in for the output of an asynchronous operation. 

Many developers employ the method “unsafeRunSnyc()” in a project when using other testing frameworks. This is a common technique that allows them to describe effects while also running them. 

However, it is also resource-intensive. Moreover, developers can run into setbacks, like a behaviour being described but not running because they forgot to use “unsafeRunSync()”. Deadlocks also pose a problem. They occur when two resources overlap in their attempts to use different processes, causing both to stall.

Using ZIO, which has an awareness of effects, helps avoid these setbacks. The result is a “cleaner” and more efficient testing process. In the example above, the method “testM” is aware of the effects and able to run them within its scope.

zio test
Examples of implementations within the ZIO Test environment

ZIO Test also provides default implementations of all types of services, such as “clock” or “random,” within the environment. This helps developers to make complex calculations quickly, such as computations related to time passage.

Features of ZIO Test

After providing a brief overview of how to set up a test, Marcin turned his attention to specific features. He explained how they can be used by programmers and the different benefits they provide. 

Resources

You may have a very expensive layer or resource you want to create once and then reuse many times throughout your codebase. To make an effectful test aware of this, simply provide a custom layer. The implementation of the layer shown below informs your test on how to acquire, when to release, and how to handle the resource.

zio test
Example of a resource with a custom layer

Shared Resources

If you have an expensive layer or resource for sharing between different files and packages, ZIO Test can handle that. Create the resource itself and define all the details—how to create, acquire, and release the resource.

Using the “provideLayerShared()” method that comes with ZIO Test makes things much more manageable. And it’s a solution that’s fully composable. 

In addition, resources can be acquired once and released after a process is completed. This overcomes several problems. For example, if a test results in either a pass or fail but a resource hasn’t been used or released. 

You can use the same layer in other places in the codebase. This is a great advantage for large organizations because only one instance of coding is required. The layer can be reused whenever needed. 

zio test
An example of shared resources

Generators

Zio Test offers an excellent package of generators. Generators are helpful if you require property-based testing in your codebase. Easily generate data types like strings and IDs. It’s even possible to combine them into custom case classes, depending on your needs. 

zio test
Code for importing generators

Property-Based Testing

Property-based testing with ZIO Test will result in code snippet similar to the sample below. 

zio test
Code example for property-based testing using generators

Use generators to check whether the class or objects have been created correctly. Do they contain concrete values? Or Is the password the same length before and after hashing? ZIO Test also offers keywords to check values such as “name.”

Aspects

Aspects are another valuable feature of ZIO Test. Among other outcomes, you can achieve the following aspects:

  • Make tests flaky or nonFlaky.
  • Timeout tests. 
  • Ignore tests. 
  • Make tests platform-specific, for example, using jvmOnly.
  • Execute an effect after the test is finished. 

Using aspects is straightforward. Simply annotate your test or suite with pre-given annotations. You can choose as many annotations as you want and chain them together. 

Assertions

zio test
Assertions coded using assertions or the “assetEqual()” method

Assertions enable you to test part of your code and receive a boolean result. You can use assertions to check that you are getting the value you expect. 

ZIO Test allows for a flexible coding style. Marcin believes that programmers can be poets. The assertion pictured above is an example of “poetic” coding. 

Alternatively, opt for the “assertEqual()” method that compares two values (instead of learning a new domain-specific language (DSL)). However, you will need to stay up to date with methods if you plan to use them. 

Exception Testing

zio test
A code example of exception testing

Exception testing is common in Scala. To include an exception, use “ZIO.fail()”. The expected failure message and the resulting failure message can be compared. 

Marcin believes that the code pictured above, while a helpful example, doesn’t fully express the true benefits of exception testing with ZIO Test. The real value is seen when individual iterations are extended to a larger codebase with many different exceptions. 

Reporting

Marcin likes to have clear reports outlining the results of every test he conducts. A record that identifies which parts of a program have either failed or passed provides crucial insights and maintains an efficient development flow. 

When all of the tests pass, there’s no problem. However, when some of the code fails, a detailed and comprehensive report of what went wrong is invaluable. 

References

Marcin used several references to help form his talk:

Additionally, Marcin is available on Twitter, LinkedIn, Github, and email.

The Last Word on ZIO Tests

ZIO Test has a lot to offer. It is well organized, open-source, and easy to read. The interop package also enables transcription from one effect to another. What’s more, the ZIO Test community is constantly growing. It’s an easy framework for developers to learn and there are plenty of resources to help educate in-house teams. 

Marcin urges everyone to explore and have fun working with ZIO Test because. He promises that it will be an enriching experience for developers. 

If your company is looking for a friendly team of software developers with the expertise and experience needed to streamline your development workflow, Scalac is here to help. Get in touch today. 

Read also:

Functional Programming vs Object-Oriented Programming

Authors

Agata Nowak
Agata Nowak

Marketing Specialist at Scalac. At work I value creativity, willngness to learn and good teamwork. Besides, I love to travel, I am piano player and #catlover.

Latest Blogposts

28.06.2022 / By Jorge Vasquez

A Prelude of Purity: Scaling Back ZIO

ZIO World is the leading annual global ZIO-based event created by Ziverge. The event aims to inspire new trends, promote innovation, and reveal significant developments across the ZIO ecosystem. ZIO World invites developers to share their expertise in using ZIO. This year, ZIO World hosted Scalac developer and ZIO contributor Jorge Vásquez. His presentation focused on […]

20.06.2022 / By John Jimenez

Functional Programming vs OOP

As a young, bright-eyed, bushy-tailed engineer starting my career at NASA in the 90s, I was fortunate enough to develop engineering-oriented software that modeled the different parts of the International Space Station. The million-line codebase was based on objects. Almost every part of the space station was represented as an object, from the overall segments […]

14.06.2022 / By Łukasz Gajowy

OpenTelemetry from a bird’s eye view: a few noteworthy parts of the project

OpenTelemetry provides you with a set of tools, integrations, APIs, and SDKs in different languages to more easily increase the observability of your application. We figured that, since we’re working on an OpenTelemetry agent extension called Mesmer, we could show you the project from a developer’s perspective and point you to the parts that could […]

Need a successful project?

Estimate project