Search code examples
pythonpytestintegration-testing

Integration Test Assertion Best Practice


I recently implemented integration tests on my legacy app, i get confused whe do assertion in Integration Test.

in unit test we can easily mock object and its make sense to only have 1 assertion every test. but when it becomes Integration Test, I want to verify the multiple scenarios and need to check every step is correct.

is it okay to do multiple assertions ? or is there some best practice to do this ?

here is my test code :

   def test_new_requested_depth_purchase_create_transaction(
        self,
        requested_depth_purchase_request_1: DepthPurchaseRequestViewModel,
    ):
        """depth_purchase_created_job() should create new lead,transaction and depth transaction package"""

        self._depth_purchase_request_service.depth_purchase_created_job(
            requested_depth_purchase_request_1.original_id
        )

        crm_depth_purchase = self._depth_purchase_repo.get_by_original_id(
            requested_depth_purchase_request_1.original_id
        )
        assert crm_depth_purchase is not None

        lead = self._lead_repo.get_by_id(crm_depth_purchase.leads_id)

        assert lead is not None

        transaction = self._transaction_repo.get_by_lead_id(lead.id)

        assert transaction is not None

        transaction_packages = self._transaction_package_repo.get_all_by_transaction_id(
            transaction.id
        )

        assert len(transaction_packages) > 0
        assert all(
            [
                trx_package.type == ProductPackageConstants.ALACARTE
                and trx_package.status_id == 0
                for trx_package in transaction_packages
            ]
        )

Test Framework : Pytest


Solution

  • Yes or no, it depends. The name "integration test" is poorly defined, there are many overlapping meanings.
    It is simpler to define the extremes : unit tests usually have only one assertion, while end-to-end tests have many. With that in mind, there is strong reason wheter to prefer having multiple asserts or only one.

    Another way to answer the question is based on the code schema "Given-When-Then". I could reorganize your code like that :

    def test_new_requested_depth_purchase_create_transaction(
        self,
        requested_depth_purchase_request_1: DepthPurchaseRequestViewModel,
    ):
        """depth_purchase_created_job() should create new lead,transaction and depth transaction package"""
        # When
        self._depth_purchase_request_service.depth_purchase_created_job(
            requested_depth_purchase_request_1.original_id
        )
    
        # Then
        crm_depth_purchase = self._depth_purchase_repo.get_by_original_id(
            requested_depth_purchase_request_1.original_id
        )
        assert crm_depth_purchase is not None
    
        lead = self._lead_repo.get_by_id(crm_depth_purchase.leads_id)
        assert lead is not None
    
        transaction = self._transaction_repo.get_by_lead_id(lead.id)
        assert transaction is not None
    
        transaction_packages = self._transaction_package_repo.get_all_by_transaction_id(
            transaction.id
        )
        assert len(transaction_packages) > 0
        assert all(
            [
                trx_package.type == ProductPackageConstants.ALACARTE
                and trx_package.status_id == 0
                for trx_package in transaction_packages
            ]
        )
    

    There is no "Given" because it is taken care of by the fixture requested_depth_purchase_request_1. If the model was not injected, it would have to be created, which result in something like :

    def test_new_requested_depth_purchase_create_transaction(self):
        # Given
        requested_depth_purchase_request_1 = some_way_to_construct_it().maybe_in_several_steps()
        
        # When
        ...
    

    In theory, you should not have asserts in your Given and When (your testing code should work properly), only in the Then because that is what the test is meant to tests. But it could serve as sanity checks.

    For me, what you do is fine. Do what suits you (or your team).