Skip to content

Burla — LLM Migration Reference

This is a self-contained reference for LLMs (or any automated tool) to convert .NET mocking code from Moq or NSubstitute to Burla. Paste this file into your system prompt or provide it as context.

This reference stays short enough to include next to the real test and production code, so the model can focus on the code under test instead of spending most of its context on mocking migration rules.

Namespace

using Burla;

Remove: using Moq;, using Moq.Protected;, using NSubstitute;, using NSubstitute.ExceptionExtensions;

Core types

Type Purpose
Mock Static entry point. Mock.Of<T>() creates a mock wrapper; Mock.Create<T>() / Mock.CreateLoose<T>() return the runtime instance directly.
IMock<T> Mock wrapper. Has .Instance (preferred), .Object (compatibility alias), .CallBase, .Setup(...), .SetupGet(...), .SetupSet(...), .CallsTo(...), .Verify(...), .Event(...).Emit(...), .VerifyNoOtherCalls(), .Reset(), .RecordedCalls.
MockSequence Ordered verification helper created with Mock.Sequence() and used via .InSequence(sequence).
Arg Argument matchers: Arg.Any<T>(), Arg.Is<T>(predicate), Arg.IsIn(...), Arg.IsNotIn(...), Arg.IsNull<T>(), Arg.IsNotNull<T>(), Arg.Ref<T>.Any.
MockBehavior Strict (default, throws on unconfigured calls) or Loose (returns defaults).
Times Never(), Once(), AtLeastOnce(), AtLeast(n), AtMost(n), Exactly(n), Between(from, to).
CallRecord Recorded call: .Method, .Arguments, .ReturnValue, .Timestamp, .GetArgument<T>(index).
UnexpectedCallException Thrown in strict mode for unconfigured calls. Includes a public ClosestMatches list of SetupInfo entries showing likely misconfigured setups ranked by relevance.
VerificationException Thrown by Verify on count mismatch.
SequenceExhaustedException Thrown when a return sequence is exhausted and no alternative exhaustion behavior was configured.

Setup patterns

Class mocking (abstract and concrete classes)

// Abstract class
var mock = Mock.Of<MyAbstractService>();
mock.Setup(x => x.GetData()).Returns("mocked");

// Concrete class with virtual methods
var mock = Mock.Of<ConcreteService>();
mock.Setup(x => x.VirtualMethod()).Returns(42);

// With constructor arguments
var mock = Mock.Of<StorageBase>("connectionString", 30);
mock.Setup(x => x.Connect()).ReturnsAsync(true);

// With behavior and constructor arguments
var mock = Mock.Of<StorageBase>(MockBehavior.Loose, "connectionString", 30);

Method returning a value

var mock = Mock.Of<IService>();
mock.Setup(x => x.GetValue(Arg.Any<int>())).Returns(42);
mock.Setup(x => x.GetValue(Arg.Is<int>(n => n > 0))).Returns(999);
mock.Setup(x => x.GetValue(5)).Returns(100);  // exact match
var result = mock.Instance.GetValue(5);

Void method

mock.Setup(x => x.DoSomething(Arg.Any<string>()));                    // allow call (strict mode)
mock.Setup(x => x.DoSomething(Arg.Any<string>())).Callback(() => {}); // with untyped callback
mock.Setup(x => x.DoSomething(Arg.Any<string>()))
    .Callback<string>(msg => captured = msg);                         // typed callback
mock.Setup(x => x.DoSomething("bad")).Throws<InvalidOperationException>();

Typed callbacks

// Void method — typed callback captures arguments
mock.Setup(x => x.Send(Arg.Any<string>()))
    .Callback<string>(msg => captured = msg);

mock.Setup(x => x.SendToUser(Arg.Any<string>(), Arg.Any<string>()))
    .Callback<string, string>((userId, msg) => { });

// Return method — typed callback runs after value is produced
mock.Setup(x => x.Add(Arg.Any<int>(), Arg.Any<int>()))
    .Returns(0)
    .Callback<int, int>((a, b) => { capturedA = a; capturedB = b; });

Overloads: Callback<T1> through Callback<T1, T2, T3, T4>.

Typed Returns (argument forwarding)

mock.Setup(x => x.Add(Arg.Any<int>(), Arg.Any<int>()))
    .Returns<int, int>((a, b) => a + b);

mock.Setup(x => x.GetDataAsync(Arg.Any<int>()))
    .Returns<int>(id => Task.FromResult($"data-{id}"));

Overloads: Returns<T1> through Returns<T1, T2, T3, T4>.

Factory-based Throws

mock.Setup(x => x.Divide(Arg.Any<int>(), Arg.Any<int>()))
    .Throws<int, int, InvalidOperationException>((a, b) =>
        new InvalidOperationException($"Cannot divide {a} by {b}"));

Overloads: Throws<T1, TException> through Throws<T1, T2, T3, T4, TException>.

Property

mock.Setup(x => x.Name).Returns("test");
mock.SetupGet(x => x.Name).Returns("test"); // Moq-compatible alias

Sequence

mock.Setup(x => x.Next()).ReturnsSequence(1, 2, 3);          // throws after exhaustion by default
mock.Setup(x => x.Next()).ReturnsSequence(1, 2).ThenReturns(0);
mock.Setup(x => x.Next()).ReturnsSequence(1, 2).ThenThrows<InvalidOperationException>();

Async

mock.Setup(x => x.GetAsync(1)).Returns("data");                               // smart auto-wrap into Task<T>
mock.Setup(x => x.GetAsync(1)).ReturnsAsync("data");                           // explicit (same)
mock.Setup(x => x.GetAsync(1)).Returns(Task.FromResult("data"));               // fully explicit
mock.Setup(x => x.GetCountAsync()).Returns(42);                                // smart auto-wrap into ValueTask<T>
mock.Setup(x => x.GetCountAsync()).ReturnsAsync(42);                            // explicit (same)

IAsyncEnumerable

mock.Setup(x => x.StreamAsync(Arg.Any<CancellationToken>()))
    .ReturnsAsyncEnumerable("a", "b", "c");

mock.Setup(x => x.StreamAsync(Arg.Any<CancellationToken>()))
    .ReturnsAsyncEnumerable(async (yield, ct) =>
    {
        for (int i = 0; i < 5; i++)
        {
            ct.ThrowIfCancellationRequested();
            await yield($"item-{i}");
        }
    });

mock.Setup(x => x.StreamAsync(Arg.Any<CancellationToken>()))
    .ReturnsAsyncEnumerable("a", "b", "c")
    .WithDelayBetweenItems(TimeSpan.FromMilliseconds(50));

Out parameters

mock.Setup(x => x.TryParse("42", out Arg.Ref<int>.Any))
    .Returns(true)
    .SetsByRefParameter(1, 42);

Ref parameters

mock.Setup(x => x.Transform(ref Arg.Ref<string>.Any))
    .SetsByRefParameter(0, "NEW_VALUE");

Events

// Subscribe to an event
mock.Instance.MessageReceived += (sender, msg) => { /* handle */ };

// Emit the event with Burla's native event API
mock.Event(nameof(IEventPublisher.MessageReceived)).Emit(mock.Instance, "Hello!");

// Emit an event with EventArgs (e.g., PropertyChangedEventArgs)
mock.Event(nameof(INotifyPropertyChanged.PropertyChanged)).Emit(mock.Instance, new System.ComponentModel.PropertyChangedEventArgs("PropertyName"));

mock.Event(name).Emit(...) is Burla's event API: it throws for unknown event names, is a no-op when the named event has no subscribers, and requires the exact handler argument count for the event delegate signature.

Verification patterns

Return value with callback

mock.Setup(x => x.GetValue(1)).Returns(42).Callback(() => callCount++);
mock.Setup(x => x.GetValue(Arg.Any<int>()))
    .Returns(42)
    .Callback<int>(id => lastId = id);

Arg matchers in helper methods

Arg matchers work from helper methods, not just directly in lambdas:

// This helper method works in Setup and CallsTo expressions
private static string IsNonEmpty() => Arg.Is<string>(s => !string.IsNullOrEmpty(s));

mock.Setup(x => x.Send(IsNonEmpty())).Callback(() => { });
var calls = mock.CallsTo(x => x.Send(IsNonEmpty()));

Verification patterns

// Query and assert (preferred for Burla-native tests)
Assert.Single(mock.CallsTo(x => x.Method()));
Assert.Empty(mock.CallsTo(x => x.Method()));
Assert.Equal(3, mock.CallsTo(x => x.Method(Arg.Any<int>())).Count);

// Verify (optional helper, mainly for low-diff Moq migrations or VerifyNoOtherCalls)
mock.Verify(x => x.Method(), Times.Once());
mock.Verify(x => x.Method(), Times.Never());
mock.Verify(x => x.Method(Arg.Any<int>()), Times.Exactly(3));

// Verify no other calls were made (calls must be verified via Verify first)
mock.Verify(x => x.Method(), Times.Once());
mock.VerifyNoOtherCalls();

// Ordered verification across one or more mocks
var otherMock = Mock.Of<IOtherService>();
using var sequence = Mock.Sequence();
mock.Setup(x => x.Step1()).InSequence(sequence);
otherMock.Setup(x => x.Step2()).InSequence(sequence);
mock.Instance.Step1();
otherMock.Instance.Step2();
// Out-of-order calls or unobserved steps throw VerificationException.

// Reset mutable state (setups, recorded calls, verification tracking, event subscriptions)
// The mock object itself and settings like CallBase are unchanged.
mock.Reset();

// Inspect recorded calls
var calls = mock.RecordedCalls;
var arg0 = calls[0].GetArgument<string>(0);

Moq → Burla translation rules

Apply these transformations in order:

  1. Namespace: using Moq;using Burla;. Remove using Moq.Protected;.
  2. Creation: new Mock<T>()Mock.Of<T>(MockBehavior.Loose). new Mock<T>(MockBehavior.Strict)Mock.Of<T>(). Use mock.Instance as the preferred runtime value; mock.Object can stay in low-diff Moq migrations.
  3. Class mocking: new Mock<AbstractClass>()Mock.Of<AbstractClass>(MockBehavior.Loose). new Mock<ConcreteClass>(arg1, arg2)Mock.Of<ConcreteClass>(MockBehavior.Loose, arg1, arg2).
  4. Matchers: Prefer Arg.Any<T>(), Arg.Is<T>(pred), Arg.IsIn(...), Arg.IsNotIn(...), Arg.IsNotNull<T>(), and Arg.Ref<T>.Any. Keeping It.* is also valid as low-cost Moq compatibility sugar.
  5. Property getters: mock.SetupGet(x => x.Prop) can stay as mock.SetupGet(x => x.Prop) or be normalized to mock.Setup(x => x.Prop).
  6. Sequences: mock.SetupSequence(x => ...).Returns(a).Returns(b)mock.Setup(x => ...).ReturnsSequence(a, b).
  7. Async: mock.Setup(...).ThrowsAsync(ex)mock.Setup(...).Throws(ex) (Burla auto-wraps). mock.Setup(...).ReturnsAsync(val)mock.Setup(...).Returns(val) (smart auto-wrap) or keep .ReturnsAsync(val).
  8. Verify: mock.Verify(x => x.M(), Times.Once())Assert.Single(mock.CallsTo(x => x.M())) (preferred) or keep mock.Verify(x => x.M(), Times.Once()) for a low-diff Moq port.
  9. Out params: Remove delegate types. out It.Ref<T>.IsAnyout Arg.Ref<T>.Any, add .SetsByRefParameter(index, value).
  10. Ref params: Same as out. ref It.Ref<T>.IsAnyref Arg.Ref<T>.Any, add .SetsByRefParameter(index, value).
  11. Callbacks: Callback<T1, T2>((a, b) => ...).Callback<T1, T2>((a, b) => ...) (typed callbacks are supported).
  12. Invocations: mock.Invocationsmock.RecordedCalls.
  13. VerifyNoOtherCalls: mock.VerifyNoOtherCalls()mock.VerifyNoOtherCalls() (identical).
  14. Reset: mock.Reset()mock.Reset().

NSubstitute → Burla translation rules

Apply these transformations in order:

  1. Namespace: using NSubstitute;using Burla;. Remove using NSubstitute.ExceptionExtensions;.
  2. Creation: Substitute.For<T>() → split into var mock = Mock.Of<T>(MockBehavior.Loose); and var sub = mock.Instance;. Replace all direct usage of the substitute variable with mock.Instance, or use Mock.CreateLoose<T>() when you do not need the wrapper later.
  3. Class creation: Substitute.For<AbstractClass>()var mock = Mock.Of<AbstractClass>(MockBehavior.Loose);. Substitute.For<ConcreteClass>(arg1, arg2)var mock = Mock.Of<ConcreteClass>(MockBehavior.Loose, arg1, arg2);.
  4. Setup: sub.Method(args).Returns(val)mock.Setup(x => x.Method(args)).Returns(val).
  5. Matchers: Arg.Any<T>(), Arg.Is<T>(pred) — same! Add Arg.IsIn(...), Arg.IsNotIn(...), Arg.IsNull<T>(), Arg.IsNotNull<T>(), Arg.Ref<T>.Any as needed.
  6. Void methods: sub.When(x => x.Method(args)).Do(...)mock.Setup(x => x.Method(args)).Callback(...).
  7. Throw on void: sub.When(x => x.M()).Do(_ => throw new Ex())mock.Setup(x => x.M()).Throws<Ex>().
  8. Exceptions: sub.Method().Throws(ex)mock.Setup(x => x.Method()).Throws(ex).
  9. Sequences: sub.Method().Returns(a, b, c)mock.Setup(x => x.Method()).ReturnsSequence(a, b, c).
  10. Verification: sub.Received().Method()Assert.Single(mock.CallsTo(x => x.Method())). sub.DidNotReceive().Method()Assert.Empty(mock.CallsTo(x => x.Method())). sub.Received(n).Method()Assert.Equal(n, mock.CallsTo(x => x.Method()).Count).
  11. Call inspection: sub.ReceivedCalls()mock.RecordedCalls. call.GetArguments()[i]call.GetArgument<T>(i).
  12. Out params: x[1] = value; return true; pattern → .Returns(true).SetsByRefParameter(1, value).
  13. Arg.Do<T>: Arg.Do<T>(callback).Callback<T>(callback) on the setup.
  14. ClearReceivedCalls: sub.ClearReceivedCalls()mock.Reset() (note: also clears setups, verification tracking, and event subscriptions; it does not change CallBase).

Important behavioral differences

  • Strict by default: Burla throws UnexpectedCallException for calls without a setup. Moq defaults to loose. NSubstitute is always loose.
  • Sequence exhaustion: Burla throws SequenceExhaustedException by default. Moq returns default(T). NSubstitute repeats the last value.
  • Last-match-wins: When multiple setups match, the last one configured wins. Same as Moq.
  • No class mocking for sealed classes: Burla can mock interfaces, abstract classes, and concrete classes with virtual methods, but sealed classes cannot be mocked.
  • Non-virtual methods are not intercepted: on concrete classes, non-virtual methods run the original implementation.
  • Event emission: mock.Event(nameof(SomeType.EventName)).Emit(sender, args...) is the public API. Unknown names throw, unsubscribed known events are ignored, and argument counts must match exactly.
  • Arg matchers in helpers: Arg.Any<T>(), Arg.Is<T>(...), etc. work when called from helper methods outside the lambda expression tree. This is useful for creating reusable matcher helpers.
  • Preferred everyday spelling: Burla-native docs prefer .Instance, Arg, Setup(...), SetsByRefParameter(...), Event(...).Emit(...), and CallsTo(...) + standard assertions for verification. .Verify(..., Times...), .Object, It, and SetupGet(...) remain available as compatibility sugar for migrations.