#How to write tests for a Python-based Dagger module?

1 messages · Page 1 of 1 (latest)

karmic sail
#

I am writing tests for my Dagger module now. Here's what the directory structure looks like

tree -L 2 -a
.
├── .dagger
│   ├── .gitattributes
│   ├── .gitignore
│   ├── pyproject.toml
│   ├── sdk
│   ├── src
│   └── uv.lock
├── LICENSE
├── README.md
├── dagger.json
└── tests
    ├── .gitattributes
    ├── .gitignore
    ├── dagger.json
    ├── pyproject.toml
    ├── sdk
    ├── src
    └── uv.lock

src/localstack_dagger_module/main.py has this basic code:

@object_type
class LocalstackDaggerModule:
    @function
    def serve(
        self, 
        ...
    ) -> dagger.Service:
        ...
            
        # Return as service
        return container.as_service()

I am adding tests for it in tests/src/tests/main.py as the docs recommend:

import dagger
from dagger import dag, function, object_type
import requests


@object_type
class Tests:
    @function
    async def test_localstack_health(self) -> str:
        """Test if LocalStack starts and responds to health check"""
        # Start LocalStack using the module
        service = await dag.LocalstackDaggerModule().serve()
....

Running this from root results in

dagger call test-localstack-health
✔ connect 0.2s
✔ load module 0.8s
✘ parsing command line arguments 0.0s
! unknown command "test-localstack-health" for "dagger call"

and within tests dir it results in

dagger call test-localstack-health
✔ connect 0.2s
✔ load module 0.7s
✔ parsing command line arguments 0.0s

✔ tests: Tests! 0.9s
✘ .testLocalstackHealth: String! 0.8s
! 'Client' object has no attribute 'LocalstackDaggerModule'

Pretty sure that I'm setting the directory structure wrong, but would love to get some help since I followed the docs itself: https://docs.dagger.io/api/module-tests/

hearty glacier
#

so if you move to your tests folder and run dagger install ../ that should fix the issue

#

your IDE should automatically complete the localstack_dagger_module function automatically for you

karmic sail
#

Hi @hearty glacier — That fixed my issue. Thank You.

#

@hearty glacier @manic sleet — Would very much appreciate any reference on how to write tests for Dagger for this specific case. I have my Dagger function (reference: https://gist.github.com/HarshCasper/bcd626ba52814a95435642d4535c4699) that starts the LocalStack container and exposes it on port 4566.

This is what my test looks like

@object_type
class Tests:
    @function
    def test_localstack_health(self) -> str:
        """Test if LocalStack starts and responds to health check"""
        # Start LocalStack using the module
        service = dag.localstack_dagger_module().serve()
        
        try:
            # Query the health endpoint
            response = requests.get("http://localhost:4566/_localstack/info") # or host.docker.internal:4566/....
            response.raise_for_status()
            
            # Validate response
            info = response.json()
            if "version" in info:
                return "Success: LocalStack is healthy"
            else:
                raise Exception("LocalStack info endpoint missing version field")
                
        except requests.RequestException as e:
            raise Exception(f"Failed to connect to LocalStack: {str(e)}")
        except ValueError as e:
            raise Exception(f"Invalid JSON response from LocalStack: {str(e)}")
        except Exception as e:
            raise Exception(f"Test failed: {str(e)}")

Now the test repeatedly fails since it cannot connect to my LocalStack container that should be ideally running since I called the serve() function. I have tried adding a retry/wait mechanism to wait for the container to be healthy, but no luck. On terminal, dagger call serve up works fine but I guess most devs would be using this module through a programming lang (I'm yet to draw up a mental picture of how and where Dagger would work, so any advice would be beneficial as well 🙏 )

karmic sail
#

Alright, I needed to throw couple of lines of this after creating the service to make this work:

await service.start()
endpoint = await service.endpoint()

I didn't find any mention of this on docs but the design & paradigm of the Dagger engine is pretty interesting.

hearty glacier
cosmic jackal
#

Yes, opened issues to update both

hearty glacier