artiomchi / FlexLabs.Upsert

FlexLabs.Upsert is a library that brings UPSERT functionality to common database providers for Entity Framework in their respective native SQL syntax
MIT License
534 stars 81 forks source link

Unit test against Upsert #103

Open djmnz opened 3 years ago

djmnz commented 3 years ago

Thanks for the extension! Saved me heaps!

If I have a class consuming this extension, what is the recommended method to unit test the On method, and the WhenMatched?

For example: validate the correct On values, and the correct updated values when matched.

Any suggestions?

artiomchi commented 3 years ago

Heya,

Hmm. There's no easy way to stub it, since the library generates SQL statements that it sends to the server, rather than using EF's internal state management.

I guess the best way to test your service is to use an InMemory database, and then check the data after the updates? The upsert library supports the InMemory provider, and I use it (alongside the other providers) in the library's unit tests

Would that work in your case, or do you have a different scenario where a different approach is needed?

ToniaDemchuk commented 2 years ago

Can we at least have interfaces instead of classes with internal constructors (for UpsertCommandBuilder)?

nwoolls commented 2 years ago

Note that all of MS's guidance for unit testing and EF Core strongly discourage using the in-memory provider for unit testing:

https://docs.microsoft.com/en-us/ef/core/testing/choosing-a-testing-strategy

As an alternative to SQLite, EF Core also comes with an in-memory provider. Although this provider was originally designed to support internal testing of EF Core itself, some developers use it as a database fake when testing EF Core applications. Doing so is highly discouraged

https://docs.microsoft.com/en-us/ef/core/testing/testing-without-the-database#inmemory-provider

As discussed in the testing overview page, using the in-memory provider for testing is strongly discouraged

nwoolls commented 2 years ago

FWIW I was able to handle this by extending my repository pattern a bit with the following implementation:


/// <inheritdoc />
public async Task<int> Upsert(Models.Widget widget)
{
    return await Widgets
        .Upsert(widget)
        .NoUpdate()
        .RunAsync();
}

/// <inheritdoc />
public async Task<int> Upsert(Models.Widget widget, Expression<Func<Models.Widget, Models.Widget>> updater)
{
    return await Widgets
        .Upsert(widget)
        .WhenMatched(updater)
        .RunAsync();
}

/// <inheritdoc />
public async Task<int> Upsert(Models.Widget widget, Expression<Func<Models.Widget, Models.Widget, Models.Widget>> updater)
{
    return await Widgets
        .Upsert(widget)
        .WhenMatched(updater)
        .RunAsync();
}