Skip to main content Link Search Menu Expand Document (external link)

Quickstart

Installing

Install DivertR as a NuGet package:

Install-Package DivertR

Or via the .NET command line interface:

dotnet add package DivertR

Creating Proxies

The Redirect<TTarget> class is used to create and manage DivertR proxies. Its basic usage is similar to other common mocking frameworks:

using DivertR;

public class QuickstartExample
{
    [Fact]
    public void RedirectTestSample()
    {
        // Create a Foo instance named "MrFoo"
        IFoo foo = new Foo("MrFoo");
        Assert.Equal("MrFoo", foo.Name);

        // Create an IFoo Redirect
        var fooRedirect = new Redirect<IFoo>();

        // Use the Redirect to create an IFoo proxy that wraps the Foo instance above as its root target
        IFoo fooProxy = fooRedirect.Proxy(foo);

        // By default proxies transparently forward calls to their root targets
        Assert.Equal("MrFoo", fooProxy.Name);

        // Intercept proxy calls and change behaviour by adding one or more 'Via' delegates to the Redirect
        fooRedirect
            .To(x => x.Name)
            .Via(() => "redirected");

        // The Redirect diverts proxy calls to its Via delegates
        Assert.Equal("redirected", fooProxy.Name);

        // Reset the Redirect and revert the proxy to its default transparent behaviour
        fooRedirect.Reset();
        Assert.Equal("MrFoo", fooProxy.Name);

        // Via delegates can access call context and e.g. relay the call to the root target
        fooRedirect
            .To(x => x.Name)
            .Via(call => call.CallRoot() + " redirected");

        Assert.Equal("MrFoo redirected", fooProxy.Name);

        // A Redirect can create any number of proxies
        var fooTwo = new Foo("FooTwo");
        IFoo fooTwoProxy = fooRedirect.Proxy(fooTwo);

        // Vias added to the Redirect are applied to all its proxies
        Assert.Equal("FooTwo redirected", fooTwoProxy.Name);

        // Reset is applied to all proxies.
        fooRedirect.Reset();
        Assert.Equal("MrFoo", fooProxy.Name);
        Assert.Equal("FooTwo", fooTwoProxy.Name);

        // A proxy with no root target returns default values
        var fooMock = fooRedirect.Proxy();
        Assert.Null(fooMock.Name);

        // Proxy calls and be recorded for verifying
        var fooCalls = fooRedirect.Record();
        
        Assert.Equal("MrFoo", fooProxy.Name);
        Assert.Equal("FooTwo", fooTwoProxy.Name);
        Assert.Null(fooMock.Echo("test"));
        
        // The recording is a collection containing details of the calls
        Assert.Equal(3, fooCalls.Count);
        
        // This can be filtered with expressions for verifying
        Assert.Equal(1, fooCalls.To(x => x.Echo(Is<string>.Any)).Count);

        // Take an immutable snapshot of the currently recorded calls to verify against
        var snapshotCalls = fooCalls.To(x => x.Name).Verify();
        Assert.Equal(2, snapshotCalls.Count);

        Assert.Equal("MrFoo", snapshotCalls[0].Return);
        Assert.Equal("FooTwo", snapshotCalls[1].Return);
    }
}

Dependency Injection Integration

DivertR is designed to be embedded easily and transparently into dependency injection containers like Microsoft.Extensions.DependencyInjection.IServiceCollection.


[Fact]
public void ServiceCollectionDemoTest()
{
    // Instantiate a Microsoft.Extensions.DependencyInjection.IServiceCollection
    IServiceCollection services = new ServiceCollection();

    // Register some services
    services.AddTransient<IFoo, Foo>();
    services.AddSingleton<IBarFactory, BarFactory>();
    services.AddSingleton<IEtc, Etc>();

    // Build a Diverter instance by registering services you want to be able to redirect
    var diverter = new DiverterBuilder()
        .Register<IFoo>()
        .Register<IBarFactory>()
        .Create();

    // Install Diverter into the ServiceCollection
    services.Divert(diverter);

    // Build an IServiceProvider as usual
    IServiceProvider provider = services.BuildServiceProvider();

    // Resolve services from the ServiceProvider as usual
    var foo = provider.GetRequiredService<IFoo>();
    var fooTwo = provider.GetRequiredService<IFoo>();

    // In its initial state DivertR is transparent and the behaviour of resolved services is unchanged
    fooTwo.Name = "FooTwo";
    Assert.Equal("original", foo.Name);
    Assert.Equal("FooTwo", fooTwo.Name);

    // Get a Redirect from the Diverter instance and configure a Via
    diverter
        .Redirect<IFoo>()
        .To(x => x.Name)
        .Via(call => $"{call.Next.Name} redirected");

    // The behaviour of resolved service instances is now changed
    Assert.Equal("original redirected", foo.Name);
    Assert.Equal("FooTwo redirected", fooTwo.Name);

    // Reset the Diverter instance
    diverter.ResetAll();

    // The original service behaviour is restored
    Assert.Equal("original", foo.Name);
    Assert.Equal("FooTwo", fooTwo.Name);
}

WebApplicationFactory Integration

DivertR is also designed to integrate with Microsoft’s WebApplicationFactory (TestServer) and facilitates writing tests on a wired-up system like this:

[Fact]
public async Task GivenFooExistsInRepo_WhenGetFoo_ThenReturnsFoo_WithOk200()
{
    // ARRANGE
    var foo = new Foo
    {
        Id = Guid.NewGuid(),
        Name = "Foo123"
    };

    _diverter
        .Redirect<IFooRepository>() // Redirect IFooRepository calls 
        .To(x => x.GetFooAsync(foo.Id)) // matching this method and argument
        .Via(() => Task.FromResult(foo)); // via this delegate

    // ACT
    var response = await _fooClient.GetFooAsync(foo.Id);
    
    // ASSERT
    response.StatusCode.ShouldBe(HttpStatusCode.OK);
    response.Content.Id.ShouldBe(foo.Id);
    response.Content.Name.ShouldBe(foo.Name);
}

[Fact]
public async Task GivenFooRepoException_WhenGetFoo_ThenReturns500InternalServerError()
{
    // ARRANGE
    _diverter
        .Redirect<IFooRepository>()
        .To(x => x.GetFooAsync(Is<Guid>.Any))
        .Via(() => throw new Exception());

    // ACT
    var response = await _fooClient.GetFooAsync(Guid.NewGuid());

    // ASSERT
    response.StatusCode.ShouldBe(HttpStatusCode.InternalServerError);
}

For more examples and a demonstration of setting up a test harness for a WebApp like this see a WebApp Testing Sample here.

Standard Mocking

The Spy class is provided and extends Redirect to add familiar, standard mocking capability:

[Fact]
public async Task SpyTestSample()
{
    // Create an IFoo mock
    var fooMock = Spy.On<IFoo>();
    // By default mocks return dummy values (C# defaults)
    Assert.Null(fooMock.Name);
    // For async methods a Task is returned wrapping a dummy result
    Assert.Null(await fooMock.EchoAsync("test"));
    
    // Mock behaviour can be configured by adding Vias using the usual redirect fluent syntax
    Spy.Of(fooMock)
        .To(x => x.Name)
        .Via(() => "redirected");

    // Mock calls are redirected to the Via delegates
    Assert.Equal("redirected", fooMock.Name);
    
    // The spy records all mock calls
    Assert.Equal(3, Spy.Of(fooMock).Calls.Count);
    // These can be filtered with expressions for verifying
    Assert.Equal(1, Spy.Of(fooMock).Calls.To(x => x.EchoAsync(Is<string>.Any)).Count);
    
    // Spies can be reset
    Spy.Of(fooMock).Reset();
    // This resets recorded calls
    Assert.Equal(0, Spy.Of(fooMock).Calls.Count);
    // And removes all Via configurations
    Assert.Null(fooMock.Name);
}

Learn More

Continue with Documentation for more details.


Table of contents