Hot to write Unit Test case for SqlDataReader using Moq - c#

I have following methods and want to write the Unit test case for mapping result through DataReader.
public interface IIMGAdvancedSearchDBProvider
{
clsGPAdvancedSearchResult GetSearchResult(clsIMGAdvancedImageSearchCriteria searchCriteria);
}
public class clsIMGAdvancedSearchSQLDBProvider : IIMGAdvancedSearchDBProvider
{
public clsGLGJSearchResultItem GetSearchResult(clsIMGAdvancedImageSearchCriteria searchCriteria)
{
using (var sqlConnection = DfxDbConnection)
{
using (var sqlCommand = sqlConnection.CreateCommand())
{
sqlCommand.Parameters.Add("#userKey", SqlDbType.UniqueIdentifier).Value = State.WebUserKey;
sqlCommand.Parameters.Add("#SiteCode", SqlDbType.VarChar).Value = State.SiteCode;
sqlCommand.Parameters.Add("#MaxRows", SqlDbType.Int).Value = searchCriteria.MaxRows;
//Add required client, client group filter paramters
AddClientAndClientGroupFilterParameters(searchCriteria, sqlCommand);
sqlCommand.CommandType = CommandType.Text;
sqlCommand.CommandText = GetCompleteSQLStatement(searchCriteria);
var reader = sqlCommand.ExecuteReader();
return alTransaction(reader);
reader.Close();
}
}
return null;
}
private clsGLGJSearchResultItem GetJournalTransaction(SqlDataReader reader)
{
return new clsGLGJSearchResultItem
{
ClientKey = DfxUtilities.GetGuidValueFromReader(reader, "ClientKey") ?? Guid.Empty,
JournalId = DfxUtilities.GetLongValueFromReader(reader, "JournalID") ?? 0,
AccountingDate = (DateTime)reader["Date"],
JournalSource =
(enumJournalSources)
Enum.Parse(typeof(enumJournalSources), reader["Source"].ToString()),
Description = DfxUtilities.GetStringValueFromReader(reader, "Description"),
DebitAmount = DfxUtilities.GetDecimalValueFromReader(reader, "DebitAmount") ?? 0,
CreditAmount = DfxUtilities.GetDecimalValueFromReader(reader, "CreditAmount") ?? 0,
ClientCode = DfxUtilities.GetStringValueFromReader(reader, "ClientCode"),
ClientName = DfxUtilities.GetStringValueFromReader(reader, "ClientName"),
Images = GetImageItems(reader)
};
}
}
Can anybody help me to resolve this?

I think you'll be better off treating your data abstraction layer (DAL) interfaces and classes (like IIMGAdvancedSearchDBProvider and clsIMGAdvancedSearchSQLDBProvider) as components that you integration test rather than unit test.
In other words, combine testing of the database schema, triggers, seed test data + your DAL implementation class (including the call to ExecuteReader). DALs should be thin and exclude business logic as yours does. You'd typically setup/teardown test data in the database for these integration tests. You might also want to create a common base class for these DAL integration test classes to setup your test database connection and perhaps some SQL logging. Might also maintain a separate test database with edge test case data or otherwise inject this data in setup/teardowns. No Mocking here.
Then you can unit test the business layer classes above it by mocking the values returned by your DAL interface IIMGAdvancedSearchDBProvider in the components/classes that use the DAL interface. I often try to complete the DAL testing first and capture some snapshots of "real production data" cases that I then return from my Mock DAL objects to the business layer unit tests. This avoids creating Mocks that miss the real data edge cases that are in the production data.
If you're using NUnit, consider using their TestCase, Pairwise and Combinatorial attributes to generated test cases (rather than mocking them) for your business layer objects. You might also find my ShouldBe wrapper library handy.
BTW ... Your cls class prefix naming convention is non-standard.

Related

How to mock constructor in c# xUnit test case?

Hi I am working on C# application. I am writing unit test case for controller level. Below is my controller code.
public IActionResult TriggerProductEventCheck([FromQuery(Name = "timeout-secs")] int timeoutS = 120)
{
int productEventsCount = 0;
if (productService.ProcessingEnabled)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(timeoutS * 1000);
lock (SyncObject)
{
this.consumerClient.Subscribe(this.productEventTopicName);
while (!cts.IsCancellationRequested)
{
var productEvent = this.eventDispatcher.Consume();
long kafkaOffSet = productEvent.Offset.Value;
Product product = new Product(productEvent, log);
if (product.Options == null)
{
break;
}
if (product != null)
{
productService.ProcessProductEvents(product, kafkaOffSet);
}
productEventsCount++;
}
}
}
Below is my unit test case.
public void ShouldReturnIfNoSQSEvents()
{
var productEventController = MockProvider.Target<ProductEventController>();
productEventController.GetDependency<IProductEventService>().ProcessingEnabled.Returns(true);
productEventController.GetDependency<IEventDispatcher>().Consume().Returns(new Confluent.Kafka.ConsumeResult<string, Avro.Generic.GenericRecord>());
productEventController.GetDependency<IConsumerClient>().Subscribe("test");
var productclass = Substitute.For<Product>();
var response = productEventController.Target.TriggerProductEventCheck() as JsonResult;
((int)response.StatusCode).ShouldBe(200);
}
Whenever I run above unit test case, Control goes inside Product product = new Product(productEvent, log); I want to mock this particular line. May I know how to handle this condition? Any help would be appreciated. Thanks
There are at least two ways.
The most advisable: add a second method that allows you to inject the desired elements. You can call one method from the other to reuse code. This second method would be used for testing. The other shuld be left as is.
Another option is to use a framework that allows to implement shims for testing. For example:
- Moles, that "allows to replace any .NET method with a delegate". Here you can see an example that allows to mock DateTime constructor: Nikolai Tillmann: Moles - Replace any .NET method with a delegate.
- "Microsot fakes": Isolating Code Under Test with Microsoft Fakes.
- There are some commercial testing frameworks that also support shims.
However, you should use the first proposed solution, or change the desing of your software, for example using Dependency Injection, so that you can easily control de dependencies in your unit testing. You should leave the use of shims for cases where you don't have any other option, for example injecting changes in third party libraries for which you don't have the option to modify their code.
You can google ".NET shims testing" to learn more about this technique.

.NET Core how to unit test service?

I have build a WebAPI and want to create a unit test project to have my services tested automatically.
The flow of my WebAPI is simple:
Controller (DI Service) -> Service (DI Repository) -> _repo CRUD
Suppose I have a service like:
public int Cancel(string id) //change status filed to 'n'
{
var item = _repo.Find(id);
item.status = "n";
_repo.Update(item);
return _repo.SaveChanges();
}
And I want to build a unit test, which just use InMemoryDatabase.
public void Cancel_StatusShouldBeN() //Testing Cancel() method of a service
{
_service.Insert(item);
int rs = _service.Cancel(item.Id);
Assert.Equal(1, rs);
item = _service.GetByid(item.Id);
Assert.Equal("n", item.status);
}
I've searched other related question, found that
You can't use dependency injections on test classes.
I just want to know if there is any other solution to achive my unit test idea?
When unit testing, you should just supply all the dependencies of the class you are testing explicitly. That is dependency injection; not having the service construct its dependencies on its own but making it rely on the outer component to provide them. When you are outside of a dependency injection container and inside a unit test where you are manually creating the class you are testing, it’s your responsibility to provide the dependencies.
In practice, this means that you either provide mocks or actual objects to the constructor. For example, you might want to provide a real logger but without a target, a real database context with a connected in-memory database, or some mocked service.
Let’s assume for this example, that the service you are testing looks like this:
public class ExampleService
{
public ExampleService(ILogger<ExampleService> logger,
MyDbContext databaseContext,
UtilityService utilityService)
{
// …
}
// …
}
So in order to test ExampleService, we need to provide those three objects. In this case, we will do the following for each:
ILogger<ExampleService> – we will use a real logger, without any attached target. So any call on the logger will work properly without us having to provide some mock, but we do not need to test the log output, so we do not need a real target
MyDbContext – Here, we’ll use the real database context with an attached in-memory database
UtilityService – For this, we will create a mock which just setups the utility method we need inside the methods we want to test.
So a unit test could look like this:
[Fact]
public async Task TestExampleMethod()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// using Moq as the mocking library
var utilityServiceMock = new Mock<UtilityService>();
utilityServiceMock.Setup(u => u.GetRandomNumber()).Returns(4);
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<Customer>().Add(new Customer()
{
Id = 2,
Name = "Foo bar"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new ExampleService(logger, db, utilityServiceMock.Object);
// act
var result = service.DoSomethingWithCustomer(2);
// assert
Assert.NotNull(result);
Assert.Equal(2, result.CustomerId);
Assert.Equal("Foo bar", result.CustomerName);
Assert.Equal(4, result.SomeRandomNumber);
}
}
In your specific Cancel case, you want to avoid using any methods of the service you are not currently testing. So if you want to test Cancel, the only method you should call from your service is Cancel. A test could look like this (just guessing the dependencies here):
[Fact]
public async Task Cancel_StatusShouldBeN()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<SomeItem>().Add(new SomeItem()
{
Id = 5,
Status = "Not N"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new YourService(logger, db);
// act
var result = service.Cancel(5);
// assert
Assert.Equal(1, result);
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
var item = db.Set<SomeItem>().Find(5);
Assert.Equal(5, item.Id);
Assert.Equal("n", item.Status);
}
}
Btw. note that I’m opening up a new database context all the time in order to avoid getting results from the cached entities. By opening a new context, I can verify that the changes actually made it into the database completely.

Unit Test for calling stored procedure

I have a method that calls the stored procedure using Entity Framework and returns the query result.
How I can write a unit test for this method? I am not sure how mock the call and return values.
Stored procedure is returning me 3 columns.
public VerifyUser VerifyUser(string accountKey)
{
try
{
LoansContext loansContext = new LoansContext();
VerifyUser queryResult = null;
using (loansContext)
{
using (loansContext.Database.Connection)
{
loansContext.Database.Connection.Open();
string sqlStatment = string.Format("{0} #AccountKey = '{1}'", "execute [Loan].[Proc_VerifyUser]", accountKey);
queryResult = loansContext.Database
.SqlQuery<VerifyUser>(sqlStatment)
.Single();
}
}
}
catch (Exception exception)
{
LoansDomainTrace.Trace.Error(EventId.Retrieve,
() => string.Format("VerifyUser exception: {0}", exception.Message), exception);
throw;
}
}
In such cases I like to unit test my method logic and then isolate the code I want to test (in your case VerifyUser) from external resources (e.g. DBs). I good option is with some sort of in-memory EF provider, however a much more common way is to abstract away your EF implementation e.g. with some sort of repository pattern. Without this isolation any tests you write are integration, not unit tests.

How to unit test code that includes a database transaction

How to put unit around the below codes:
public DbContextTransaction QTTransactionBegin()
{
return Database.BeginTransaction();
}
public int CreateCampaign(CreateCampaignModel createCampaignModel)
{
using (var transaction = _qtContext.QTTransactionBegin())
{
try
{
var campaign = new Campaign();
campaign.CampaignCode = createCampaignModel.CampaignCode;
campaign.CampaignDescription = createCampaignModel.CampaignDescription;
campaign.CampaignDate = createCampaignModel.CampaignDate;
campaign.CampaignNotes = createCampaignModel.CampaignNotes;
campaign.OwnerUserID = createCampaignModel.OwnerUserID;
campaign.AddedOn = DateTime.Now;
campaign.AddedBy = createCampaignModel.OwnerUserName;
campaign.UpdatedOn = DateTime.Now;
campaign.UpdatedBy = createCampaignModel.OwnerUserName;
campaign.CampaignSegments = GetCampaignSegmentList(createCampaignModel);
var campaignId = AddOrUpdateCampaign(campaign);
transaction.Commit();
return campaignId;
}
catch (Exception ex)
{
transaction.Rollback();
}
}
return 0;
}
Could anyone advise me how to put unit test around above code ?
I tried the code as below :
Database_database;
[TestInitialize]
public void SetUp()
{
_qtDALMock = _factory.CreateMock<IQTDAL>();
_campaignRepository = new CampaignRepository(_qtDALMock.MockObject);
}
[TestMethod]
public void Check_CreateCampaign_Test()
{
// arrange
const int expectedCampaignId = 1;
var createCampaign = QueryToolDummies.CreateCampaignModel;
_database.BeginTransaction();
_qtDALMock.Expects.One.MethodWith(x => x.QTTransactionBegin())
.WillReturn(_database.BeginTransaction());
_qtDALMock.Expects.One.Method(x => x.AddOrUpdateCampaign(null))
.With(Is.TypeOf<Campaign>())
.WillReturn(expectedCampaignId);
// act
var result = _campaignRepository.CreateCampaign(createCampaign);
// assert
Assert.IsNotNull(result);
}
this _database.BeginTransaction() has a problem. the error says can't use like it.
Please advise.
One question? Why are you trying to start a transaction into a unit test?
Will be easy if you Mock your repository using Moq framework and return what you need to return from the repo.
In fact, I thought that start a BeginTransaction() in a unit test is not a good practice.
I hope this helps
I've experienced the same issue, it's quite tricky to work around.
I tried creating a wrapper class for the context that exposes a BeginTransaction() method, but ultimately you end up needing to mock the DbContextTransaction returned by BeginTransaction() when it comes to testing, but DbContextTransaction has neither an interface or a public constructor.
In the end I wrote a transaction manager class that creates and manages its own transaction and exposes methods for beginning, committing and rolling back the transaction. That manager class, and the service that returns it, can then be faked allowing the code using transactions to be fully tested.
I've written that up fully in this answer.
You are trying to unit test more than one unit.
Assuming that the code above is your 'data layer' / repository then you are performing an integration test because more than a single unit is involved in the test.
You could include setup / teardown for the database within your test class and call the SUT (subject under test) with valid and invalid data to validate expected behaviours.

Is Mocking able to replace functionality wrapped inside a method?

I'm trying to define a way to simulate an case on accessing into a database without accessing... That's probably sounds quite crazy, but it's not.
Here is an example about a method i would like to test:
public IDevice GetDeviceFromRepository(string name)
{
IDevice device = null;
IDbConnection connection = new SqlConnection(ConnectionString);
connection.Open();
try
{
IDbCommand command = connection.CreateCommand();
command.CommandText = string.Format("SELECT DEVICE_ID,DEVICE_NAME FROM DEVICE WHERE DEVICE_NAME='{0}'", name);
IDataReader dataReader = command.ExecuteReader();
if(dataReader.NextResult())
{
device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));
}
}
finally
{
connection.Close();
}
return device;
}
I'm pretending to mock IDataReader so i can control what's being read. Something like that (using Moq framework):
[TestMethod()]
public void GetDeviceFromRepositoryTest()
{
Mock<IDataReader> dataReaderMock = new Mock<IDataReader>();
dataReaderMock.Setup(x => x.NextResult()).Returns(true);
dataReaderMock.Setup(x => x.GetInt32(0)).Returns(000);
dataReaderMock.Setup(x => x.GetString(1)).Returns("myName");
Mock<IDbCommand> commandMock = new Mock<IDbCommand>();
commandMock.Setup(x => x.ExecuteReader()).Returns(dataReaderMock.Object);
Mock<RemoveDeviceManager> removeMock = new Mock<RemoveDeviceManager>();
removeMock.Setup()
RemoveDeviceManager target =new RemoveDeviceManager(new Device(000, "myName"));
string name = string.Empty;
IDevice expected = new Device(000, "myName"); // TODO: Initialize to an appropriate value
IDevice actual;
actual = target.GetDeviceFromRepository(name);
Assert.AreEqual(expected.SerialNumber, actual.SerialNumber);
Assert.AreEqual(expected.Name, actual.Name);
}
My question is whether i can force method GetDeviceFromRepository to replace IDataReader by mocked one.
Although you're currently use Moq I think that the functionality you're looking for cannot be achived without dependency injection unless you use Typemock Isolator (Disclaimer - I worked at Typemock).
Isolator has a feature called "future objects" that enable replacing a future instantiation of an object with a previously created fake object:
// Create fake (stub/mock whateever) objects
var fakeSqlConnection = Isolate.Fake.Instance<SqlConnection>();
var fakeCommand = Isolate.Fake.Instance<SqlCommand>();
Isolate.WhenCalled(() => fakeSqlConnection.CreateCommand()).WillReturn(fakeCommand);
var fakeReader = Isolate.Fake.Instance<SqlDataReader>();
Isolate.WhenCalled(() => fakeCommand.ExecuteReader()).WillReturn(fakeReader);
// Next time SQLConnection is instantiated replace with our fake
Isolate.Swap.NextInstance<SqlConnection>().With(fakeSqlConnection);
I would think the problem here is your direct dependency ultimately to SqlConnection. If you would use some variant of dependency injection such that your code gets access to the IDbCommand without knowing how it gets constructed, you would be able to inject your mock without much of a hassle.
I understand this doesn't quite answer your question, but in the long run doing stuff as described will give you far better testability.
I agree with Frank's answer that moving toward Dependency Injection is the better long-term solution, but there are some intermediate steps you can take to move you in that direction without biting the whole thing off.
One thing is to move the construction of the IDbConnection class into a protected virtual method inside your class:
protected virtual IDbConnection CreateConnection()
{
return new SqlConnection(ConnectionString);
}
Then, you can create a testing version of your class like so:
public class TestingRemoteDeviceManager : RemoteDeviceManager
{
public override IDbConnection CreateConnection()
{
IDbConnection conn = new Mock<IDbConnection>();
//mock out the rest of the interface, as well as the IDbCommand and
//IDataReader interfaces
return conn;
}
}
That returns a Mock or fake IDbConnection instead of the concrete SqlConnection. That fake can then return a fake IDbCommand object, which can then return a fake IDataReader object.
The mantra is test until fear is transformed in boredom. I think you've crossed that line here. If you would take control of the data reader, then the only code you're testing is this:
device = new Device(dataReader.GetInt32(0),dataReader.GetString(1));
There is almost nothing to test here, which is good: the data layer should be simple and stupid. So don't try to unit test your data layer. If you feel you must test it, then integration-test it against a real database.
Of course, hiding your data layer behind a IDeviceRepository interface so that you can easily mock it in order to test other code is still a good idea.

Categories