Testing

Rays eliminates the pain of testing singletons by integrating natively with Ginkgo and Gomega.

Import the testing package and testing suites directly into your test files.

Automatic Container Isolation

The TestingContainer hooks into Ginkgo’s lifecycle. After every It() block, the container is wiped clean. Singletons do not bleed between tests.

Targeted Wiring

By Instead of scanning the whole application, you manually register exactly the Beams you need for the unit under test using AddBeamFor().

import (
    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    . "github.com/BeamFoundry/rays/pkg/testing"

    // Will detect all beams in  myproject/pkg/services
    . "github.com/mygroup/myproject/pkg/services"
)

var _ = Describe("UserService", func() {

    BeforeEach(func() {
        // Use the TestingContainer to mock YAML configuration for the test
        GetContainer().LoadConfigAt(map[string]any{
            "dsn": "mock://db",
        }, "database")

        // 1. Will load all the Stereotype imported in services
        GetContainer().EnableScanning()
    })

    It("should correctly wire the mocked database", func() {

        // 2. Initialize the isolated container
        err := GetContainer().Initialize()
        Expect(err).NotTo(HaveOccurred())

        // 3. Fetch the wired instance
        svc := GetBeamDefaultInstance[*UserService]()

        // 4. Assert
        Expect(svc).NotTo(BeNil())
        Expect(svc.db.dsn).To(Equal("mock://db"))
    })
})

Advanced: Mocking Prototypes (Foundry[T])

When a service depends on a Foundry[T], you often want to mock the forged instances during tests rather than relying on the real Prototype lifecycle.

Because Foundry[T] is an interface, you can implement a mock version and register it in your test to override the framework’s default factory behavior.

import (
    "context"
    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    . "github.com/BeamFoundry/rays/pkg/core"
    . "github.com/BeamFoundry/rays/pkg/lang"
    . "github.com/BeamFoundry/rays/pkg/testing"

    // Will detect all beams in  myproject/pkg/middleware
    . "github.com/mygroup/myproject/pkg/middleware"
)

// 1. Create a Mock Foundry implementation
type MockSessionFoundry struct {
    Prototype
    Component // Register as a standard test Component
    MockSession *SessionState
}

func (m *MockSessionFoundry) WithContext(_ context.Context) (*SessionState, Error) {
    return m.MockSession, nil
}

var _ = Describe("AuthMiddleware", func() {
    It("should use the mocked foundry", func() {
        // 2. Will load all the Stereotype imported in services including AuthMiddleware
        GetContainer().EnableScanning()

        // 3. Instantiate and inject the mock instead of the real Prototype
        mockFoundry := &MockSessionFoundry{
            MockSession: &SessionState{userID: "mock-user-123"},
        }
        //
        AddBeamFor(mockFoundry)

        err := GetContainer().Initialize()
        Expect(err).NotTo(HaveOccurred())

        middleware := GetBeamDefaultInstance[*AuthMiddleware]()

        // When middleware.HandleHTTP is called, it will use the mock-user-123 session.
    })
})