Skip to main content

Next-gen Arrange-Act-Assert to structure test cases and force software engineers to express explicit intentions in BDD style.

Table of content:

Introduction

intentions is a Python next-gen Arrange-Act-Assert library created to help structuring test cases and force software engineers to express explicit intentions in BDD style.

Arrange-Act-Assert is a pattern used to structure test cases. It provides a clear way to organize test code, making it easier to read and understand:

  • Arrange block sets up the necessary objects, data, or state before the action to be tested.
  • Act block performs the actual operation or method call that should be tested.
  • Assert block checks the results of the action comparing the actual outcome with the expected outcome.

Behavior-Driven Development is an approach in testing that emphasizes the behavior of an application for business needs. It focuses on defining test cases in plain, simple language with use cases and user stories. It typically uses the Given-When-Then format to specify the system's expected behavior in various situations:

  • Given a context.
  • When an action happens.
  • Then an expected outcome.

intentions aims to combine those two approaches to empower software engineers for more effective testing with when, case, and expect context managers using which they can build behavior-driven arrange-act-assert based test cases.

In the same time you are not required to use it everywhere, even in the single test you are able to define which constructs to use and how many of them. For instance, you can skip it for unit tests and only use for integration tests.

class TestAccountService:

    def test_transfer_money_with_insufficient_balance(self):
        mock_send_in_app_notification = mock('notifications.send')

        receiver_account = AccountFactory()

        with when('Sender account has insufficient balance'):
            sender_account = AccountFactory(balance=0)

        with when('Pushing in-app notifications feature flag is enabled for sender account'):
            enable_in_app_notifications(account=sender_account)

        with case('Transfer money from one account to another'):
            AccountService.transfer_money(from=sender_account, to=receiver_account, amount=100)

        with expect('No transfers have been made'):
            assert not sender_account.transfers
            assert not receiver_account.transfers

        with expect('Increase credit limit proposal is created'):
            assert Proposal.get_last(account=sender_account, type=ProposalType.INCREASE_CREDIT_LIMIT)
            
        with expect('Sender account receives insufficient balance in-app notification'):
            mock_send_in_app_notification.assert_called_with(
                account_id=sender_account.id,
                type=InAppNotificationType.INSUFFICIENT_BALANCE,
                expired_at=None,
            )

Motivation

Clarity and readability

when, case and expect clearly convey the purpose of each block. With them, it is easy to tell a story of what is being tested, making it easier for someone reading the test to understand the intention behind each part. Instead of limited # Arrange, # Act and # Assert comments in your test case, you can use as much intentions as possible emphasizing on every important detail of the test case.

Mental Load

With when, case and expect, test cases are broken down clearly, reducing mental load.

Maintenance

If the test fails, the when, case and expect make it easier to understand why the test was written in the first place due to its descriptive nature. As the test case evolves, it's easier to maintain because the purpose of each step is clear.

BDD Encouragement

It aligns with Behavior-Driven Development principles, as it focuses on describing the behavior of the system under a test case, making the tests more focused on outcomes and behavior rather than implementation details.

Collaboration

With when, case and expect, you introduce a common language for communication between developers, testers, and business stakeholders and make collaboration easier. It also helps to minimize the learning curve for new joiners.

Getting Started

How to install

Install the library with the following command using pip3:

$ pip3 install intentions

Usage

When

It emphasizes on setting up the necessary objects, data, or state to make the context of a test cace meaningful. Important to use this construct to focus exactly specific condition of the test case.

As you see on the example below, the context manager is used only on specifically a user from the UK that has uploaded a document for a verification:

from intentions import when


class TestDocumentVerificationService:
  
  def test_verify_document_when_uploaded_and_user_from_uk(self):
      admin = UserFactory(is_admin=True)
      verification = VerificationFactory()
      
      ...
    
      with when('User is from the United Kingdom'):
          user = UserFactory(country=Country.UK)

      with when('User document is uploaded'):
          user_document = Document(
              user=user,
              verification=verification,
              status=DocumentStatus.UPLOADED,
          )

      ...

Case

It emphasizes on performing the actual operation or method call that should be tested. Important to use this context manager over exact execution.

As you see on the example below, the context manager is used only for the document verification method named verify_document. Besides having many other functions such as mocks, data preparation and side functions in the test alongside the method:

from intentions import case


class TestDocumentVerificationService:
  
  def test_verify_document_when_uploaded_and_user_from_uk(self):
      ...
      
      mock_verification_api_response = prepare_verification_api_response()
      mock_document_verification_api_request = mock(
          path='api.document_verification.request',
          data=mock_verification_api_response,
      )
      
      enable_async_requests()
      create_user_kyc_profile()

      with case('Verify a document'):
          document_verification = DocumentVerificationService.verify_document(
              document=document, 
              user=user, 
              admin=admin,
          )

      ...

Expect

It emphasizes on checking the results of the action comparing the actual outcome with the expected outcome, expected behavior or change in the system. Important to use this context manager to emphasize different groups of expectations and exact outcome or behavior.

As you see on the example below, the context manager is used to distinguish 3 different outcomes: the user's document was reviewed by external API, was additionally reviewed by admin as user is from the UK and the document verification happened without errors:

from intentions import expect


class TestDocumentVerificationService:
  
  def test_verify_document_when_uploaded_and_user_from_uk(self):
      ...
      
      with expect('Document was verified by external API'):
          mock_document_verification_api_request.assert_called()
          assert document.is_verified_by_provider
  
      with expect('Document was additionally verified by admin as user is from the UK'):
          assert document.admin == admin
          assert document.is_verified_by_admin

      with expect('Document verification happened without errors'):
          assert not document_verification.errors

Metadata

Release files for intentions 0.0.10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for intentions 0.0.10
File Size Uploaded Provenance
intentions-0.0.10.tar.gz 11.9 kB Without provenance Details

Release files / intentions-0.0.10.tar.gz

Download URL intentions-0.0.10.tar.gz
Size 11.9 kB
Tags Source
SHA-256 checksum
How to use checksums
cd4c923c137420451cd2477d950d56217e6ec697d3054fe036725c03090119e0
BLAKE2b-256 checksum
How to use checksums
c64b17896be519bcc522f553b20d4041767bc464b21b4cd546e756758f9af957
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.11.10

Security for intentions 0.0.10

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page