rotestify

package module
v0.0.0-...-e745bd2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 6, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

Testify Plugin

The testify plugin provides testing utilities for reactive streams using the testify assertion library.

Installation

go get github.com/samber/ro/plugins/testify

Usage

Basic Testing
import (
    "testing"
    "github.com/samber/ro"
    rotestify "github.com/samber/ro/plugins/testify"
    "github.com/stretchr/testify/assert"
)

func TestObservable(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Just(1, 2, 3, 4, 5)
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectNext(1).
        ExpectNext(2).
        ExpectNext(3).
        ExpectNext(4).
        ExpectNext(5).
        ExpectComplete().
        Verify()
}
Testing with Error Handling
func TestObservableWithError(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Pipe1(
        ro.Just(1, 2, 3),
        ro.MapErr(func(n int) (int, error) {
            if n == 2 {
                return n, errors.New("error on 2")
            }
            return n, nil
        }),
    )
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectNext(1).
        ExpectError(errors.New("error on 2")).
        Verify()
}
Testing Sequences
func TestObservableSequence(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Just("a", "b", "c")
    
    rotestify.Testify[string](is).
        Source(observable).
        ExpectNextSeq("a", "b", "c").
        ExpectComplete().
        Verify()
}
Testing with Custom Messages
func TestObservableWithMessages(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Just(42)
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectNext(42, "expected the answer to life").
        ExpectComplete("should complete after one value").
        Verify()
}

Advanced Usage

Testing Filtered Streams
func TestFilteredObservable(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Pipe1(
        ro.Just(1, 2, 3, 4, 5, 6),
        ro.Filter(func(n int) bool {
            return n%2 == 0 // Keep only even numbers
        }),
    )
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectNextSeq(2, 4, 6).
        ExpectComplete().
        Verify()
}
Testing Mapped Streams
func TestMappedObservable(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Pipe1(
        ro.Just("hello", "world"),
        ro.Map(func(s string) string {
            return strings.ToUpper(s)
        }),
    )
    
    rotestify.Testify[string](is).
        Source(observable).
        ExpectNextSeq("HELLO", "WORLD").
        ExpectComplete().
        Verify()
}
Testing Error Scenarios
func TestErrorScenarios(t *testing.T) {
    is := assert.New(t)
    
    t.Run("immediate error", func(t *testing.T) {
        observable := ro.Throw[int](errors.New("test error"))
        
        rotestify.Testify[int](is).
            Source(observable).
            ExpectError(errors.New("test error")).
            Verify()
    })
    
    t.Run("error after values", func(t *testing.T) {
        observable := ro.Pipe1(
            ro.Just(1, 2, 3),
            ro.MapErr(func(n int) (int, error) {
                if n == 3 {
                    return n, errors.New("error on 3")
                }
                return n, nil
            }),
        )
        
        rotestify.Testify[int](is).
            Source(observable).
            ExpectNext(1).
            ExpectNext(2).
            ExpectError(errors.New("error on 3")).
            Verify()
    })
}
Testing with Context
func TestObservableWithContext(t *testing.T) {
    is := assert.New(t)
    ctx := context.Background()
    
    observable := ro.Just(1, 2, 3)
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectNextSeq(1, 2, 3).
        ExpectComplete().
        VerifyWithContext(ctx)
}

Testing Patterns

Testing Empty Streams
func TestEmptyObservable(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Empty[int]()
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectComplete().
        Verify()
}
Testing Single Value Streams
func TestSingleValueObservable(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Just(42)
    
    rotestify.Testify[int](is).
        Source(observable).
        ExpectNext(42).
        ExpectComplete().
        Verify()
}
Testing Infinite Streams
func TestInfiniteObservable(t *testing.T) {
    is := assert.New(t)
    
    observable := ro.Interval(100 * time.Millisecond)
    
    rotestify.Testify[ro.IntervalValue](is).
        Source(observable).
        ExpectNext(ro.IntervalValue(0)).
        ExpectNext(ro.IntervalValue(1)).
        ExpectNext(ro.IntervalValue(2)).
        // Note: Don't call ExpectComplete() for infinite streams
        Verify()
}

Best Practices

Use Descriptive Test Names
func TestUserService_GetActiveUsers_ReturnsOnlyActiveUsers(t *testing.T) {
    is := assert.New(t)
    
    observable := userService.GetActiveUsers()
    
    rotestify.Testify[User](is).
        Source(observable).
        ExpectNextSeq(
            User{ID: "1", Name: "Alice", Active: true},
            User{ID: "2", Name: "Bob", Active: true},
        ).
        ExpectComplete().
        Verify()
}
Test Error Conditions
func TestUserService_GetUser_ReturnsErrorForInvalidID(t *testing.T) {
    is := assert.New(t)
    
    observable := userService.GetUser("invalid-id")
    
    rotestify.Testify[User](is).
        Source(observable).
        ExpectError(errors.New("user not found")).
        Verify()
}
Test Edge Cases
func TestDataProcessor_ProcessData_HandlesEmptyInput(t *testing.T) {
    is := assert.New(t)
    
    observable := dataProcessor.ProcessData([]string{})
    
    rotestify.Testify[ProcessedData](is).
        Source(observable).
        ExpectComplete().
        Verify()
}

Integration with Other Testing Libraries

Using with Testify Suite
import (
    "github.com/stretchr/testify/suite"
)

type ObservableTestSuite struct {
    suite.Suite
}

func (suite *ObservableTestSuite) TestBasicObservable() {
    observable := ro.Just(1, 2, 3)
    
    rotestify.Testify[int](suite.Assert()).
        Source(observable).
        ExpectNextSeq(1, 2, 3).
        ExpectComplete().
        Verify()
}

func TestObservableTestSuite(t *testing.T) {
    suite.Run(t, new(ObservableTestSuite))
}

Dependencies

This plugin requires the testify library:

go get github.com/stretchr/testify

Limitations

  • The testing framework is designed for synchronous testing
  • Infinite streams should not call ExpectComplete()
  • Error testing requires exact error matching
  • Context-aware testing is supported but not required

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Testify

func Testify[T any](is *assert.Assertions) testing.AssertSpec[T]

Testify creates a new instance of test. It is used to assert the behavior of an observable sequence.

Inspired by Flux.

Example
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that emits values
observable := ro.Just(1, 2, 3, 4, 5)

// Test the observable behavior
Testify[int](is).
	Source(observable).
	ExpectNext(1).
	ExpectNext(2).
	ExpectNext(3).
	ExpectNext(4).
	ExpectNext(5).
	ExpectComplete().
	Verify()

fmt.Println("Test passes if observable emits exactly 1, 2, 3, 4, 5 and completes")
Output:

Test passes if observable emits exactly 1, 2, 3, 4, 5 and completes
Example (ComplexPipeline)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create a complex pipeline: filter -> map -> take
observable := ro.Pipe3(
	ro.Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
	ro.Filter(func(n int) bool {
		return n%2 == 0 // Keep only even numbers
	}),
	ro.Map(func(n int) int {
		return n * 2 // Double the values
	}),
	ro.Take[int](3), // Take only first 3 values
)

// Test the complex pipeline behavior
Testify[int](is).
	Source(observable).
	ExpectNextSeq(4, 8, 12). // 2*2, 4*2, 6*2
	ExpectComplete().
	Verify()

fmt.Println("Test passes if pipeline filters even numbers, doubles them, and takes first 3: 4, 8, 12")
Output:

Test passes if pipeline filters even numbers, doubles them, and takes first 3: 4, 8, 12
Example (Context)
// Create a test instance
t := &testing.T{}
is := assert.New(t)
ctx := context.Background()

// Create an observable that emits values
observable := ro.Just(1, 2, 3)

// Test the observable behavior with context
Testify[int](is).
	Source(observable).
	ExpectNextSeq(1, 2, 3).
	ExpectComplete().
	VerifyWithContext(ctx)

fmt.Println("Test passes if observable emits 1, 2, 3 and completes with context support")
Output:

Test passes if observable emits 1, 2, 3 and completes with context support
Example (CustomMessages)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that emits a single value
observable := ro.Just(42)

// Test the observable behavior with custom error messages
Testify[int](is).
	Source(observable).
	ExpectNext(42, "expected the answer to life").
	ExpectComplete("should complete after one value").
	Verify()

fmt.Println("Test passes if observable emits 42 and completes, with custom messages on failure")
Output:

Test passes if observable emits 42 and completes, with custom messages on failure
Example (Empty)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an empty observable
observable := ro.Empty[int]()

// Test that empty observable completes without emitting values
Testify[int](is).
	Source(observable).
	ExpectComplete().
	Verify()

fmt.Println("Test passes if observable completes without emitting any values")
Output:

Test passes if observable completes without emitting any values
Example (Error)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that emits an error
observable := ro.Pipe1(
	ro.Just(1, 2, 3),
	ro.MapErr(func(n int) (int, error) {
		if n == 2 {
			return n, errors.New("error on 2")
		}
		return n, nil
	}),
)

// Test the observable behavior with error handling
Testify[int](is).
	Source(observable).
	ExpectNext(1).
	ExpectError(errors.New("error on 2")).
	Verify()

fmt.Println("Test passes if observable emits 1, then error \"error on 2\"")
Output:

Test passes if observable emits 1, then error "error on 2"
Example (ErrorOnly)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that only emits an error
observable := ro.Throw[string](errors.New("something went wrong"))

// Test error-only observable
Testify[string](is).
	Source(observable).
	ExpectError(errors.New("something went wrong")).
	Verify()

fmt.Println("Test passes if observable emits exactly the expected error")
Output:

Test passes if observable emits exactly the expected error
Example (ErrorScenarios)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Test immediate error scenario
immediateErrorObservable := ro.Throw[int](errors.New("test error"))

Testify[int](is).
	Source(immediateErrorObservable).
	ExpectError(errors.New("test error")).
	Verify()

// Test error after values scenario
errorAfterValuesObservable := ro.Pipe1(
	ro.Just(1, 2, 3),
	ro.MapErr(func(n int) (int, error) {
		if n == 3 {
			return n, errors.New("error on 3")
		}
		return n, nil
	}),
)

Testify[int](is).
	Source(errorAfterValuesObservable).
	ExpectNext(1).
	ExpectNext(2).
	ExpectError(errors.New("error on 3")).
	Verify()

fmt.Println("First test passes if observable immediately emits error \"test error\"")
fmt.Println("Second test passes if observable emits 1, 2, then error \"error on 3\"")
Output:

First test passes if observable immediately emits error "test error"
Second test passes if observable emits 1, 2, then error "error on 3"
Example (Filtered)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that filters even numbers
observable := ro.Pipe1(
	ro.Just(1, 2, 3, 4, 5, 6),
	ro.Filter(func(n int) bool {
		return n%2 == 0 // Keep only even numbers
	}),
)

// Test the filtered observable behavior
Testify[int](is).
	Source(observable).
	ExpectNextSeq(2, 4, 6).
	ExpectComplete().
	Verify()

fmt.Println("Test passes if observable emits only even numbers: 2, 4, 6")
Output:

Test passes if observable emits only even numbers: 2, 4, 6
Example (Mapped)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that transforms strings to uppercase
observable := ro.Pipe1(
	ro.Just("hello", "world"),
	ro.Map(func(s string) string {
		return strings.ToUpper(s)
	}),
)

// Test the mapped observable behavior
Testify[string](is).
	Source(observable).
	ExpectNextSeq("HELLO", "WORLD").
	ExpectComplete().
	Verify()

fmt.Println("Test passes if observable transforms strings to uppercase: \"HELLO\", \"WORLD\"")
Output:

Test passes if observable transforms strings to uppercase: "HELLO", "WORLD"
Example (Sequence)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that emits a sequence
observable := ro.Just("a", "b", "c")

// Test the observable behavior using sequence expectation
Testify[string](is).
	Source(observable).
	ExpectNextSeq("a", "b", "c").
	ExpectComplete().
	Verify()

fmt.Println("Test passes if observable emits exactly \"a\", \"b\", \"c\" in sequence and completes")
Output:

Test passes if observable emits exactly "a", "b", "c" in sequence and completes
Example (SingleValue)
// Create a test instance
t := &testing.T{}
is := assert.New(t)

// Create an observable that emits a single value
observable := ro.Just("single value")

// Test single value observable
Testify[string](is).
	Source(observable).
	ExpectNext("single value").
	ExpectComplete().
	Verify()

fmt.Println("Test passes if observable emits exactly \"single value\" and completes")
Output:

Test passes if observable emits exactly "single value" and completes

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL