devlooped/moq · error · NotSupportedException

The equals ("==" or "=" in VB) and the conditional 'and'…

Error message

The equals ("==" or "=" in VB) and the conditional 'and' ("&&" or "AndAlso" in VB) operators are the only ones supported in the query specification expression. Unsupported expression: {0}

What it means

Moq's LINQ-to-Mocks (Mock.Of / query syntax) translates the query expression into mock setups via MockSetupsBuilder.VisitBinary. Inside a specification, only Equal (==) and AndAlso (&&) binary operators can be translated; anything else (>, <, !=, ||, +, etc.) throws NotSupportedException. This is a deliberate limitation of the simple LINQ provider, not a bug.

Solutions

  1. Rewrite the predicate using only == and && (e.g. replace x > 3 with an exact-value match).
  2. Invert logic: replace 'a != b' with 'a == expected' where possible.
  3. Fall back to classic setup API: mock.Setup(f => f.Bar).Returns(...).Callback(...) or Setup with It.Is<T>(predicate) matchers, which support arbitrary predicates.
  4. Use a callback/Returns delegate for complex logic instead of the LINQ query expression.

Example fix

// before
var foo = Mock.Of<IFoo>(f => f.Bar > 3);
// after
var foo = Mock.Of<IFoo>(f => f.Bar == 4);
// or
var mock = new Mock<IFoo>();
mock.Setup(f => f.Bar).Callback(() => { /* complex logic */ }).Returns(5);
Defensive patterns

Strategy: validation

Validate before calling

// Restrict Mock.Of predicates to == and &&:
// OK: f => f.A == 1 && f.B == "x"
// NOT OK: f => f.A > 1 || f.B != "x"  -> use mock.Setup + It.Is instead

Try / catch

try { var foo = Mock.Of<IFoo>(predicate); }
catch (NotSupportedException) { var mock = new Mock<IFoo>(); mock.Setup(...); foo = mock.Object; }

Prevention

When it happens

Trigger: var foo = Mock.Of<IFoo>(f => f.Bar > 3), f => f.Name != "x", or f => f.A || f.B — any query-spec binary node whose NodeType is neither Equal nor AndAlso when stackIndex > 0.

Common situations: Writing convenient range or inequality filters in Mock.Of predicates; using || for 'or' conditions; after migrating from a full LINQ provider and assuming all operators are supported.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16). Data as JSON: /api/errors/55afd36e08d7722d. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/Linq/MockSetupsBuilder.cs:32

{
    class MockSetupsBuilder : ExpressionVisitor
    {
        static readonly string[] queryableMethods = new[] { "First", "Where", "FirstOrDefault" };
        static readonly string[] unsupportedMethods = new[] { "All", "Any", "Last", "LastOrDefault", "Single", "SingleOrDefault" };

        int stackIndex;
        int quoteDepth;

        public MockSetupsBuilder()
        {
        }

        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (this.stackIndex > 0)
            {
                if (node.NodeType != ExpressionType.Equal && node.NodeType != ExpressionType.AndAlso)
                    throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.LinqBinaryOperatorNotSupported, node.ToStringFixed()));

                if (node.NodeType == ExpressionType.Equal)
                {
                    // TODO: throw if a matcher is used on either side of the expression.
                    //ThrowIfMatcherIsUsed(

                    // Account for the inverted assignment/querying like "false == foo.IsValid" scenario
                    if (node.Left.NodeType == ExpressionType.Constant)
                        // Invert left & right nodes in this case.
                        return ConvertToSetup(node.Right, node.Left) ?? base.VisitBinary(node);
                    else
                        // Perform straight conversion where the right-hand side will be the setup return value.
                        return ConvertToSetup(node.Left, node.Right) ?? base.VisitBinary(node);
                }
            }

            return base.VisitBinary(node);
        }

View on GitHub (pinned to 89a5be629c)