Skip to content

VerifyTests/Verify.SqlServer

Repository files navigation

Verify.SqlServer

Discussions Build status NuGet Status

Extends Verify to allow verification of SqlServer bits.

See Milestones for release notes.

Sponsors

Entity Framework Extensions

Entity Framework Extensions is a major sponsor and is proud to contribute to the development this project.

Entity Framework Extensions

Developed using JetBrains IDEs

JetBrains logo.

NuGet

Usage

[ModuleInitializer]
public static void Init() =>
    VerifySqlServer.Initialize();

snippet source | anchor

SqlServer Schema

This test:

await Verify(connection);

snippet source | anchor

Will result in the following verified file:


Object types to include

await Verify(connection)
    // include only tables and views
    .SchemaIncludes(DbObjects.Tables | DbObjects.Views);

snippet source | anchor

Available values:

namespace VerifyTests.SqlServer;

[Flags]
public enum DbObjects
{
    StoredProcedures = 1,
    Synonyms = 2,
    Tables = 4,
    UserDefinedFunctions = 8,
    Views = 16,
    All = StoredProcedures | Synonyms | Tables | UserDefinedFunctions | Views
}

snippet source | anchor

Filtering

Objects can be dynamically filtered:

await Verify(connection)
    // include tables & views, or named MyTrigger
    .SchemaFilter(
        _ => _ is TableViewBase ||
             _.Name == "MyTrigger");

snippet source | anchor

Recording

Recording allows all commands executed to be captured and then (optionally) verified.

Call Recording.Start():

await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
Recording.Start();
await using var command = connection.CreateCommand();
command.CommandText = "select Value from MyTable";
var value = await command.ExecuteScalarAsync();
await Verify(value!);

snippet source | anchor

Will result in the following verified file:

{
  target: 42,
  sql: {
    Text:
select Value
from   MyTable,
    HasTransaction: false
  }
}

snippet source | anchor

Sql entries can be explicitly read using Recording.Stop(), optionally filtered, and passed to Verify:

await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
Recording.Start();
await using var command = connection.CreateCommand();
command.CommandText = "select Value from MyTable";
var value = await command.ExecuteScalarAsync();

await using var errorCommand = connection.CreateCommand();
errorCommand.CommandText = "select Value from BadTable";
try
{
    await errorCommand.ExecuteScalarAsync();
}
catch
{
}

var entries = Recording
    .Stop()
    .Select(_ => _.Data);
//Optionally filter results
await Verify(
    new
    {
        value,
        sqlEntries = entries
    });

snippet source | anchor

Interpreting recording results

Recording results can be interpreted in a variety of ways:

var entries = Recording.Stop();

// all sql entries via key
var sqlEntries = entries
    .Where(_ => _.Name == "sql")
    .Select(_ => _.Data);

// successful Commands via Type
var sqlCommandsViaType = entries
    .Select(_ => _.Data)
    .OfType<SqlCommand>();

// failed Commands via Type
var sqlErrorsViaType = entries
    .Select(_ => _.Data)
    .OfType<ErrorEntry>();

snippet source | anchor

Disabling recording

Recording is enabled by VerifySqlServer.Initialize(), which subscribes to the Microsoft.Data.SqlClient diagnostic listener. Pass recordCommands: false to leave that listener unsubscribed:

[ModuleInitializer]
public static void Init() =>
    VerifySqlServer.Initialize(recordCommands: false);

snippet source | anchor

Only recording is disabled. The converters, and the SqlConnection schema file converter, are still registered.

This is useful when another package records the same commands. Verify.EntityFramework records EF Core commands under the name ef, and EF Core executes those commands through SqlCommand. So with both packages recording, every command EF executes is captured twice: once as sql and once as ef.

Two constraints are worth knowing:

  • VerifierSettings.InitializePlugins() discovers this package and calls Initialize(), which enables recording. So Initialize(recordCommands: false) has to run before InitializePlugins(), otherwise discovery initializes first and the explicit call throws Already Initialized.
  • Recording.IgnoreNames("sql") is an alternative that needs no ordering, since it can be called at any point before recording starts. It discards sql entries as they are added, but the listener stays subscribed, so each command is still cloned and then thrown away.

Icon

Database designed by Creative Stall from The Noun Project.

About

Extends Verify to allow verification of SqlServer bits.

Resources

License

Code of conduct

Stars

18 stars

Watchers

1 watching

Forks

Sponsor this project

 

Contributors