DEV Community

Andrew Chaa
Andrew Chaa

Posted on β€’ Edited on β€’ Originally published at andrewchaa.me.uk

8 1

Mocking Cosmos Container method for unit testing

In my unit/component tests, I often assert that all the correct values are passed into Cosmos Container from the api request payload. By asserting that all the parameters are correctly passed, I can make sure the endpoint is doing its job correctly.

private readonly TestApplicationFactory<Startup> _factory;
private readonly ITestOutputHelper _output;
private readonly HttpClient _client;
private readonly Fixture _fixture;

public CreateTransactionTests(TestApplicationFactory<Startup> factory, 
    ITestOutputHelper output)
{
    _factory = factory;
    _output = output;
    _client = factory.CreateClient();
    _fixture = new Fixture();
}

[Fact]
public async Task Should_store_correct_transaction_details_in_the_database_and_return_200_Ok()
{
    // Arrange
    _factory.MockContainer.Reset();

    var transactionId = Guid.NewGuid();
    var request = _fixture.Build<CreateTransactionRequest>()
        .With(x => x.Currency, "EUR")
        .Create();

    TransactionData transactionData = null;

    var mockItemResponse = new Mock<ItemResponse<TransactionData>>();

    mockItemResponse.Setup(x => x.StatusCode)
        .Returns(HttpStatusCode.OK);

    _factory.MockContainer.Setup(x => x.CreateItemAsync(It.IsAny<TransactionData>(),
            It.IsAny<PartitionKey>(),
            It.IsAny<ItemRequestOptions>(),
            default(CancellationToken)))
        .ReturnsAsync(mockItemResponse.Object)                
        .Callback<TransactionData, PartitionKey?, RequestOptions, CancellationToken>(
            (t, p, r, c) => transactionData = t);

    // Act
    var response = await _client.PutAsync($"/v1/transactions/{transactionId}", request.ToStringContent());

    // Assert
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
    Assert.Equal(request.AccountId, transactionData.AccountId);
    Assert.Equal(request.Amount, transactionData.Amount);
    Assert.Equal(request.Currency, transactionData.Currency);
    ...
}

Enter fullscreen mode Exit fullscreen mode

xUnit's IClassFixture

IClassFixture lets you use a shared class to use in your tests. I put all of my my infrastructure plumbing code there.

public class TestApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
    public Mock<Container> MockContainer;

    public TestApplicationFactory() { }

    protected override IWebHostBuilder CreateWebHostBuilder() => new WebHostBuilder();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        MockContainer = new Mock<Container>();

        builder
            .UseKestrel()
            .UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development")
            .UseContentRoot(Directory.GetCurrentDirectory())
            .ConfigureAppConfiguration((context, configBuilder) =>
            {
                configBuilder.SetBasePath(context.HostingEnvironment.ContentRootPath);
                configBuilder.AddJsonFile("appsettings.json", true);
                configBuilder.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();

        builder.ConfigureTestServices(services =>
        {
            services.AddSingleton<ITransactionRepository>(x => 
                new TransactionRepository(MockContainer.Object, new NullLogger<TransactionRepository>()));
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

I have public MockContainer that mocks Cosmos Container.

AutoFixture

AutoFixture is very handy to populate a request object.

var request = _fixture.Build<CreateTransactionRequest>()
    .With(x => x.Currency, "EUR")
    .Create();
Enter fullscreen mode Exit fullscreen mode

Set up Mock Cosmos Container

So that I can verify what parameters the Container receives. I use moq's Callback. I prefer it over Verify becasue I can assert each value per each line.

var mockItemResponse = new Mock<ItemResponse<TransactionData>>();

mockItemResponse.Setup(x => x.StatusCode)
    .Returns(HttpStatusCode.OK);

_factory.MockContainer.Setup(x => x.CreateItemAsync(It.IsAny<TransactionData>(),
        It.IsAny<PartitionKey>(),
        It.IsAny<ItemRequestOptions>(),
        default(CancellationToken)))
    .ReturnsAsync(mockItemResponse.Object)                
    .Callback<TransactionData, PartitionKey?, RequestOptions, CancellationToken>(
        (t, p, r, c) => transactionData = t);
Enter fullscreen mode Exit fullscreen mode

One thing to note is I used Mock<ItemResponse<TransactionData>>(). It's becasue ItemResponse has Internal constructor and I canot new up the class. So I used mock instead.

Assert

var response = await _client.PutAsync($"/v1/transactions/{transactionId}", request.ToStringContent());

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(request.AccountId, transactionData.AccountId);
Assert.Equal(request.Amount, transactionData.Amount);
Assert.Equal(request.Currency, transactionData.Currency);
...
Enter fullscreen mode Exit fullscreen mode

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (2)

Collapse
 
kkrishna_gopfs profile image
Kalyan Krishna β€’

Thanks, saved me a few days of work :)

Collapse
 
mariomeyrelles profile image
Mario Meyrelles β€’

Hello Andrew,

Have you been able to mock the ReadItemAsync also? Would be willing to share?

Kind Regards,
MΓ‘rio

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

πŸ‘‹ Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay