Why does (await DbConnection.QueryAsync<T>(sql)).ToList() fail for tests with bunit in some cases and in the others with two different errors?

Viewed 578

I try to write a bunit test for the component EditProposal in a Blazor server side project. I have strange behavior that my test continues without error in 1 out of 10 times and the other times I get one of the following errors:

System.InvalidOperationException : BeginExecuteReader requires an open and available Connection. The connection's current state is closed.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__188_0(Task`1 result)

and sometimes the error

System.InvalidOperationException : The connection does not support MultipleActiveResultSets.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__188_0(Task`1 result)

This is my basic test setup:

[Test]
public async Task EditProposal_ItSavesAnEditedProposal()
{
    using var ctx = new TestContext();
    ctx.Services.AddSingleton(_proposalService);
    ctx.Services.AddSingleton(_skillableService);
    ctx.JSInterop.Mode = JSRuntimeMode.Loose;

    var cut = ctx.RenderComponent<EditProposal>(parameters => 
        parameters.Add(p => p.Id, "1"));
    cut.WaitForState(() => cut.FindAll("tr").Count > 0, TimeSpan.FromSeconds(15));
    
    cut.Find("input").Change("TestTitle");
    cut.Find("textarea").Change("A new proposal description");
    cut.Find("select").Change("Beratung");
    
    cut.Find("form").Submit();

It fails for the following method:

public async Task<List<Skill>> GetAllSkillsForSkillableType(string skillableType, int skillableTypeId)
{
    var sql =
        $"select ParentId, s.Id, Type, Name  from skill s inner join Skillable sa on sa.SkillId = s.Id inner join {skillableType} p on p.Id = sa.{skillableType}Id Where {skillableType}Id = {skillableTypeId};";
    var skills = (await DbConnection.QueryAsync<Skill>(sql)).ToList();
    skills = await SetAllParents(skills);
    return skills;
}

exactly this line: var skills = (await DbConnection.QueryAsync<Skill>(sql)).ToList();

I call this method within the ProposalService which is used to load the specific proposal into my edit proposal from the database:

public async Task<Proposal> GetProposalAsync(int id)
{
    var parameters = new {Id = id};
    var sql = "SELECT * FROM proposal WHERE id = @Id";

    var result = await DbConnection.QuerySingleOrDefaultAsync<Proposal>(sql, parameters);
    if (result == null) result = new Proposal();
    result.Skills = await SkillableServiceAsync.GetAllSkillsForSkillableType("Proposal", result.Id);
    if (result.Skills.Count == 0 && result.Equals(new Proposal())) return null;
    return result;
}

It seems to be related to the dapper methods.

Something which is really strange about this that it happens only within bunit tests and never on the browser or in the integration tests for the service methods. I think I might have to mock the service altogether but it seems to be more a bug or is there anything wrong with my code?

How can I fix this without having to mock the service?

update

I was able to find a possible answer here.

Giving the second method another connection seems to make it fail in less cases. As a note: I abandoned opening connection by myself from the services with using:

services.AddTransient<IDbConnection>(sp => new SqlConnection(DatabaseUtils.ConnectionString)); in startup.cs

1 Answers

so giving the failing line an own connection solved the problem:

using IDbConnection dbConnection = new SqlConnection(DatabaseUtils.ConnectionString);
var results = (await dbConnection.QueryAsync<Skill>(sql, new { ids = Ids })).ToList();

I think it was only failing for bunit since it executes a little bit faster than the browser. The thing is my method was called two times but tried to use the same connection. So when one closed it the other still needed access.

Related