-
Notifications
You must be signed in to change notification settings - Fork 0
QueryTesting
- Create new XUnit test project in visual studio.
- Add reference to
Salix.Dapper.Cqrs.MsSql.Testing.XUnit
package. - Add reference to project, containing
IQuery
implementations.
As we need actual database with structure matching application needs - tests are considered "assembly" or "integration" tests due to requirement of external IO component (database).
Dapper.Cqrs.Testing
package contains XUnit test Fixture, which is capable of creating database connection and reusing it for all these tests (Collection Fixture). Due to some limitations in XUnit engine, there are still some things you should take care of in your test project:
Collection definition is very simple empty class with [CollectionDefinition]
attribute, which then should be put on all tests, which are to be part of database tests collection.
Here is sample of such collection definition class:
using Salix.Dapper.Cqrs.MsSql.Testing.XUnit;
// <summary>
/// Collection attribute for Database tests, which requires the same (single) setup and dispose/cleanup.
/// This attribute should be put on all tests classes which require actual database to work with.
/// (Or on base class for all such tests).
/// </summary>
[CollectionDefinition(nameof(SqlTestsCollectionAttr))]
public class SqlTestsCollectionAttr : ICollectionFixture<SqlDatabaseFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the placed on tests/test class to apply [CollectionDefinition]
}
Here SqlDatabaseFixture
is implemented fixture in Dapper.Cqrs.Testing
package.You can choose other name of this collection class - it doesn't matter. Most important is the name of collection in [CollectionDefinition(name)]
attribute. You must use exactly the same name on [Collection]
attribute on test class. Thus - best to use nameof
directive on collection class as shown in example.
And in turn - it should be based on another base class MsSqlTestBase
in Dapper.Cqrs.Testing
package. Your base class can be decorated with [Collection]
attribute to avoid doing it for all actual testing classes.
Also this base class should implement GetSqlConnectionString()
method with something returning actual SQL Database connection string for tests to use.
Here is example:
using Salix.Dapper.Cqrs.MsSql.Testing.XUnit;
using Xunit;
[Collection(nameof(SqlTestsCollectionAttr))]
public abstract class DatabaseTestsBase : MsSqlTestBase
{
protected override string GetSqlConnectionString()
{
// Should get it from some configuration!
return "Data Source=localhost;Initial Catalog=Chinook;Integrated Security=True;";
}
}
All above then gets together in actual test class.
using YourApp.Queries;
using Xunit;
using Xunit.Abstractions;
namespace YourApp
{
/// <summary>
/// Test validates all existing IQuery implementations in YourApp.Queries project
/// against database.
/// </summary>
public class QueryStatementValidationTests : DatabaseTestsBase
{
/// <summary>
/// Constructor - Should call Collection/Fixture InitializeTestContext method
/// to set up all database and logging infrastructure.
/// </summary>
/// <param name="outputHelper">The XUnit output helper (like Console.Write) Gets injected by XUnit engine during runtime.</param>
/// <param name="testFixture">The database tests fixture injected by collection definition.</param>
public QueryStatementValidationTests(ITestOutputHelper outputHelper, SqlDatabaseFixture testFixture) =>
this.InitializeTestContext(outputHelper, testFixture);
/// <summary>
/// MEGA-TEST, which looks up all IQuery implementations (using Helper)
/// It validates SqlStatement inside IQuery class by calling its Validate method.
/// </summary>
/// <param name="queryClassType">Type of the query class (supplied by method in [MemberData] attribute).</param>
/// <remarks>
/// [MemberData] attribute parameters are:
/// 1 - method to get all types of IQueryValidator classes,
/// 2 - any existing IQueryValidator class from assembly where all those classes are located,
/// 3 - Class (static), where Method (1) is to be found.
/// </remarks>
[Theory(DisplayName = "Query validate for: ")]
[MemberData(nameof(HelperQueryCommandClasses.QueryValidatorClassesForAssemblyType), typeof(AlbumByIdQuery), MemberType = typeof(HelperQueryCommandClasses))]
public void QueryClass_SqlValidate_Succeeds(Type queryClassType)
{
// parameters - we need as-if-real parameters supplied for query (if any)
object[] parameters = HelperQueryCommandClasses.CreateDummyParametersForType(queryClassType);
// Creating IQuery implementation instance object
var instance = (IQueryValidator)Activator.CreateInstance(queryClassType, parameters);
// Calling its validate method (MsSqlQueryBase.Validate(), if used)
instance.Validate(this.TestFixture.SqlSession);
}
}
}
If all is done as shown above (see also Sample solution for actual code), then executing this one single test will fins all existing IQueryValidator
(or IQuery
) implementation classes and runs a test on them.
Results are something like this:
In case you have unmatching changes in database structure (or simply - syntax error or typo in SQL statement) - test on this class will fail, indicating failure reason: