jonwagner / Insight.Database

Fast, lightweight .NET micro-ORM
Other
857 stars 145 forks source link

Unit test with Moq #463

Closed ezelans closed 2 years ago

ezelans commented 3 years ago

Describe the question

I'm trying to create some unit test without connect with the database.

So I have this method:

public async Task GetCountry(countryId){ await GetSQLConnection().QueryAsync("GetCountry", new {@CountryId = countryId}).ConfigureAwait(false) };

Can you help me to test this method?

Jaxelr commented 3 years ago

What type of error message are you getting?

Please also provide the SQLs related to the method (stored procedures, tables)

jonwagner commented 3 years ago

The only thing the method does is connect to the database and return data. I don't think you can easily test that without mocking the entire database functionality. In my opinion, you're not getting much test value out of testing that scenario.

If you really wanted to test this with Moq, you would need to:

  1. Have GetSQLConnection() return a mock SqlConnection.
  2. Have that mock return a mock Task for QueryAsync. It should behave like a task when you call configure await.
  3. Have that mock task return a mock recordset that contained the data that you are mapping.

At that point, it would return the data to Insight.Database to be mapped to a data structure. That code is already tested and unlikely to change.

A more valuable test scenario would be to use the repository pattern with InsightDB:

interface ICountryRepository {
    Task<Country> GetCountryAsync(int countryId);
}

// use the repository with:
var repo = sqlConnection.As<ICountryRepository>()
var list = await repo.GetCountryAsync(id);

// mock the repository with:
var mock = new Moq<ICountryRepository>();
mock.(setup the GetCountry response)
var repo = mock.Object;
var list = await repo.GetCountryAsync(id);

Then you're not testing the boilerplate code. You're testing how your code uses it.

keerthirajap commented 2 years ago

Also how to unit test Moq with multiple result set.. Please help.

I am using Insight.Database with Auto Interface Implementation.

` namespace RepositoryInterface { using System; using System.Text; using System.Threading.Tasks; using System.Collections.Generic;

using Insight.Database;

public interface IAuthenticationRepository
{

    [Sql("[dbo].[P_GetUserDetailsForLoginValidation]")]
    Task<Results<User, UserRole>> GetUserDetailsForLoginValidationAsync(string emailId, long userId = 0);

}

} `

jonwagner commented 2 years ago

You can create the return value like this:

var results = new Results<User, UserRole>;
results.Set1 = /* list of users */
results.Set2 = /* list of userrole */
return Task.From(results);

Then you just have to set up the Moq using moq syntax.

keerthirajap commented 2 years ago

I am getting readonly only.

image

CS0200 Property or indexer 'Results.Set1' cannot be assigned to -- it is read only
Property or indexer 'Results<long, User>.Set2' cannot be assigned to -- it is read only

jonwagner commented 2 years ago

Aha. I also think the constructor is private. We can update it to make the constructor public.

jonwagner commented 2 years ago

In 6.3.9, the Results constructors are now public:

            var results = new Results<int, int>(new List<int>() { 1, 2 }, new List<int>() { 3, 4 });

As a reminder, the more detail you provide on an issue, the faster we can resolve it.