dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'actual')

Error message

Value cannot be null. (Parameter 'actual')

What it means

ReactiveAssert.AreElementsEqual(expected, actual, comparer, message) throws ArgumentNullException with parameter name 'actual' when the actual IObservable<T> is null. The expected argument is validated first, so hitting this means expected was fine but the observed sequence was null.

Solutions

  1. Ensure the code under test returns a real observable (use Observable.Empty<T>() instead of null).
  2. Fix mock/stub setup so the tested path returns an observable.
  3. Check for null and fail with a clearer test assertion before calling AreElementsEqual.

Example fix

// before
var actual = _sut.GetStream(); // returns null
ReactiveAssert.AreElementsEqual(expected, actual, comparer);
// after
Assert.NotNull(_sut.GetStream);
var actual = _sut.GetStream() ?? Observable.Empty<int>();
ReactiveAssert.AreElementsEqual(expected, actual, comparer);
Defensive patterns

Strategy: validation

Validate before calling

if (actual == null)
    throw new InvalidOperationException("Observable under test returned null");
ReactiveAssert.AreElementsEqual(expected, actual, comparer);

Type guard

if (actual is not IObservable<int> validActual) Assert.Fail("actual is null or wrong type");

Try / catch

try
{
    ReactiveAssert.AreElementsEqual(expected, actual, comparer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "actual")
{
    Assert.Fail("SUT returned null observable — check stubs/factory");
}

Prevention

When it happens

Trigger: Calling AreElementsEqual with null actual observable — typically the observable under test returned null (e.g. a stub or factory returning null instead of Observable.Empty).

Common situations: Mocked dependencies configured without a Return value; async factory methods that can return null; tests written before the system under test was wired up.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/d463638ba5f6847d. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/Microsoft.Reactive.Testing/ReactiveAssert.cs:194

        /// <summary>
        /// Asserts that both observable sequences have equal length and equal elements.
        /// </summary>
        /// <typeparam name="T">The type of the elements in the sequence.</typeparam>
        /// <param name="expected">Expected sequence.</param>
        /// <param name="actual">Actual sequence to compare against the expected one.</param>
        /// <param name="comparer">Comparer used to check for equality.</param>
        /// <param name="message">Error message for assert failure.</param>
        /// <exception cref="ArgumentNullException"><paramref name="expected"/> or <paramref name="actual"/> is null.</exception>
        public static void AreElementsEqual<T>(IObservable<T> expected, IObservable<T> actual, IEqualityComparer<T> comparer, string message)
        {
            if (expected == null)
            {
                throw new ArgumentNullException(nameof(expected));
            }

            if (actual == null)
            {
                throw new ArgumentNullException(nameof(actual));
            }

            AreElementsEqual(expected.Materialize().ToEnumerable(), actual.Materialize().ToEnumerable(), new NotificationComparer<T>(comparer), message);
        }

        /// <summary>
        /// Asserts that the given action throws an exception of the type specified in the generic parameter, or a subtype thereof.
        /// </summary>
        /// <typeparam name="TException">Type of the exception to check for.</typeparam>
        /// <param name="action">Action to run.</param>
        /// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
        public static void Throws<TException>(Action action) where TException : Exception
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

View on GitHub (pinned to 94b5d5ab91)