How to unit test code that includes a database transaction - c#

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.

Related

.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.

Setting Up MOQ Update Test

I am developing Moq tests for various entities. I can setup create and delete tests fine, but not update - the entity in the repository does not change. I know this is due to the PersistAll doing nothing (probably due to a setup I am missing).
This is a sample of an insert persist setup (I am looking for an Update version):
agg.Setup(a => a.InsertOnPersist<Thingy>(model)).Callback(() => mockThingies.Add(model));
In addition, I also have this to link the List to being the repository:
agg.Setup(a => a.GetObjectStore<Artist>()).Returns(mockThingies.AsQueryable());
This is a sample of an update test I have:
public List<Thingy> mockThingies; //this is our repository
[TestInitialize]
public void SetupTests()
{
mockThingies= new List<Thingy>();
Thingy someThingy = new Thingy();
someThingy.Name = "MyName";
someThingy.ID = 1;
mockThingies.Add(someThingy);
}
[TestMethod]
public void CanEditExistingThingy()
{
Mock<BusinessExceptionBroadcaster> beb = new Mock<BusinessExceptionBroadcaster>();
Mock<IValidationEngine> valid = new Mock<IValidationEngine>();
Mock<IAggregate> agg = new Mock<IAggregate>();
agg.Setup(a => a.GetObjectStore<Thingy>()).Returns(mockThingies.AsQueryable());
ThingyRepository repo = new ThingyRepository (agg.Object);
ThingyService service = new ThingyService (repo, beb.Object, valid.Object);
Thingy newThingy = new Thingy();
newThingy.ID = 1; //same as old
newThingy.Name = "newname"; //new name
Assert.AreNotEqual(newThingy.Name,mockThingies[0].Name);
Assert.IsTrue(service.Update(newThingy));
Assert.AreEqual(newThingy.Name, mockThingies[0].Name); //FAILS HERE
}
This is the code to update:
public bool Update(Thingy entity)
{
Thingy existingThingy= _Thingy.FirstOrDefault(t=>t.ID == entity.ID);
if (existingThingy != null)
{
_Thingy.PersistAll();
return true;
}
else
{
//unimportant
}
}
return false;
}
Don't worry about testing whether the update call actually updates something. You'll just want to verify that your service calls the appropriate method on the repo to perform the update and persist. Testing the actual update is a little outside the scope of this one test.
As far as I can see, it can't work, because you're setting one Thingy with ID=1 in Setup, and then create other one with same ID in test. Although they share same ID, they are not same, so your changes can't be ever propagated to repository.
In fact, I think that it's a bug in your CUT code, because while you're testing ID match, you don't test that your repository knows something about entity you're updating. To add, I personally think that there's something wrong with your repository design if it allows such things.
If we were talking about EntityFramework I'd say you have to attach your entity to context.

Unit test exception handling

A seemingly simple one here. How can I test the exception handling of this code block using a unit test?
public DbFactoryResponseType Close()
{
DbFactoryResponseType dbFactoryResponse = new DbFactoryResponseType();
try
{
if (m_isConnected)
{
m_isConnected = false;
if (m_hasRecordSet)
{
m_hasRecordSet = false;
m_dbFactoryDatabaseDataReader.Close();
m_dbFactoryDatabaseDataReader.Dispose();
}
m_dbFactoryDatabaseCommand.Dispose();
m_dbFactoryDatabaseConnection.Close();
m_dbFactoryDatabaseConnection.Dispose();
}
dbFactoryResponse.ExceptionMessage = "";
dbFactoryResponse.Success = true;
dbFactoryResponse.UserFriendlyMessage = "OK";
return dbFactoryResponse;
}
catch (Exception ex)
{
dbFactoryResponse.ExceptionMessage = ex.Message;
dbFactoryResponse.Success = false;
dbFactoryResponse.UserFriendlyMessage = "Error: Error while attempting to close the database connection.";
return dbFactoryResponse;
}
}
Here is what I have so far but I do not know how to make the exception fire allowing me to test the output.
/// <summary>
/// Test method to test closing a PosgreSQL database connection.
/// </summary>
[TestMethod]
public void TestClosePostgreSQLConnectionException()
{
const string connectionString = "Server=myIp;Port=myPort;Database=myDatabase;User Id=myUser;Password=myPassword;";
const string provider = "Npgsql";
DbProviderFactoryConnection aDbProviderFactoryConnection = new DbProviderFactoryConnection(connectionString, provider);
DbFactoryResponseType dbFactoryResponseType = aDbProviderFactoryConnection.Close();
Assert.IsNotNull(dbFactoryResponseType);
Assert.AreEqual(false, dbFactoryResponseType.Success);
}
You can create a mock version of IDbFactoryDatabaseConnection (which is an interface that you will need to introduce), then in the setup of your mock, throw an exception when calling Close() on the mock and then in your test check ExceptionMessage, Success and UserFriendlyMessage.
The way you do this is to use a mocking framework like Rhino Mocks or MoQ or you could even create a mock or stub of your own. You would then, in your test inject the mock version of the class into your class constructor (assuming you are using constructor injection rather than setter injection) and then you'll be able to control the behaviour of your mock.
An example, using MoQ, of how you would do this is below:
Mock<IDbFactoryDatabaseConnection> connectionMock = new Mock<IDbFactoryDatabaseConnection>();
DbProviderFactoryConnection aDbProviderFactoryConnection = new DbProviderFactoryConnection(connectionString, provider, connectionMock.Object);
connectionMock.Setup(c => c.Close()).Throws<Exception>();
DbFactoryResponseType dbFactoryResponseType = aDbProviderFactoryConnection.Close();
Of course, in line with industry best practice this also means that you must adhere to SOLID principles, specifically the principle of Dependency Inversion, which means that you will need to create an interface for the DbFactoryDatabaseConnection class (I assume that is the name of your class), which is what I've shown in the example above.

How to rollback in EF4 for Unit Test TearDown?

In my research about rolling back transactions in EF4, it seems everybody refers to this blog post or offers a similar explanation. In my scenario, I'm wanting to do this in a unit testing scenario where I want to rollback practically everything I do within my unit testing context to keep from updating the data in the database (yeah, we'll increment counters but that's okay). In order to do this, is it best to follow the following plan? Am I missing some concept or anything else major with this (aside from my SetupMyTest and PerformMyTest functions won't really exist that way)?
[TestMethod]
public void Foo
{
using (var ts = new TransactionScope())
{
// Arrange
SetupMyTest(context);
// Act
PerformMyTest(context);
var numberOfChanges = context.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
// if there's an issue, chances are that an exception has been thrown by now.
// Assert
Assert.IsTrue(numberOfChanges > 0, "Failed to _____");
// transaction will rollback because we do not ever call Complete on it
}
}
We use TransactionScope for this.
private TransactionScope transScope;
#region Additional test attributes
//
// Use TestInitialize to run code before running each test
[TestInitialize()]
public void MyTestInitialize()
{
transScope = new TransactionScope();
}
// Use TestCleanup to run code after each test has run
[TestCleanup()]
public void MyTestCleanup()
{
transScope.Dispose();
}
This will rollback any changes made in any of the tests.

Rolling back records created by PersistenceSpecifications in Fluent NHibernate

I'm learning some Fluent NHibernate and I've run across the semi-awesome PersistenceSpecification class.
I've set it up in a unit test to verify my mappings and it works great. However, it leaves the record in the database when done. I tried throwing it in a transaction so I can rollback the changes but I get an error:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'AdoTransaction'..
Without a transaction I have to figure out the ID's of the record, retrieve them and delete them and that doesn't seem very elegant.
Any thoughts?
EDIT:
Here is the code snippet:
var factory = GetSessionFactory();
using (var session = factory.OpenSession())
using (var transaction = session.BeginTransaction())
{
new PersistenceSpecification<TimePeriod>(session)
.CheckProperty(x => x.EndDate, DateTime.Today)
.VerifyTheMappings();
transaction.Rollback();
}
Try setting the IsolationLevel on the transaction. This snippet worked for me:
using (var trans = _session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
new PersistenceSpecification<Event>(_session)
.CheckProperty(p => p.StartTime, new DateTime(2010, 1, 1))
.VerifyTheMappings();
trans.Rollback();
}
The PersistenceSpecification is usually used with an in-memory database like SQLite, that's why it doesn't roll anything back. I believe there's a constructor overload that takes an ISession instance, have you tried getting a transaction from there then rolling that back after?
I think the issue here is VerifyTheMappings() calls TransactionSave() which does a tx.Commit() to the database. As James indicated, this technique seems to work great for throw away in-memory testing techniques. This would not work in the case of testing mappings against a legacy database.
Setting IsolationLevel.ReadUncommitted will work, but only incidentally, since all it is doing is telling the session that it can read without needing a new transaction (a dirty read, in DBMS parlance) - so Session.Transaction.Commit () doesn't have to commit a database transaction before the verification reads. This also means that it is not necessarily testing what you think it is testing! (I also think this has probably-questionable support amongst non-MS SQL databases). The answer from leebrandt works because of the explicit rollback, not the isolation level (nb. At the time of the answer this helped more than it does now, see note below).
The good news is that the correct way to do this is to just rollback the transaction manually. Session.Transaction is automatically replaced whenever the transaction is committed, so you'll need to hold a reference to it, and you'll have to open one explicitly anyways, since TransactionalSave () checks if the current transaction is active and creates (and disposes!) its own if not. I typically test all of my mappings in the same fixture, where I also verify the factory creation and a few other infrastructural persistence things, so I like the following pattern for this to keep the plumbing down:
class TestFixture {
static ISessionFactory factory = CreateMyFactorySomehowHere();
ISession session;
ITransaction tx;
public void Setup ()
{
session = factory.OpenSession ();
tx = session.BeginTransaction ();
}
public void Cleanup ()
{
tx.Rollback ();
tx.Dispose ();
session.Close ();
}
public void TestAMappingForSomething ()
{
var spec = new PersistenceSpecification<Something> (session);
spec.VerifyTheMappings ();
}
}
Obviously, insert your own test-framework-specific terminology and attributes/annotations wherever, but you get the idea.
I've just now noticed how old this question is: this behavior was fixed in this commit in July 09, to handle existing transactions nicely so that the above works! Clearly this is what you were doing originally anyways.
i think that it's very important to do this testing with your real db, to see that his tables definition r ok, so i'v developed a very simple class that perform a crud test on a mapped entity and roll back at the end;
internal class GenericMappingTesterWithRealDB<T> where T : IIdentifiable
{
public T EntityToTest { get; set; }
public Func<T, object> PerformEntityManipulationBeforeUpdate { get; set; }
public GenericMappingTesterWithRealDB()
{
Assume.That(SessionFactoryProvider.NewSession,Is.Not.Null);
}
public void RunTest()
{
using (ISession session = SessionFactoryProvider.NewSession)
using (ITransaction transaction = session.BeginTransaction())
{
try
{
session.Save(EntityToTest);
var item = session.Get<T>(EntityToTest.ID);
Assert.IsNotNull(item);
if (PerformEntityManipulationBeforeUpdate != null)
{
PerformEntityManipulationBeforeUpdate.Invoke(EntityToTest);
}
session.Update(EntityToTest);
session.Delete(EntityToTest);
session.Save(EntityToTest);
}
catch (Exception e)
{
Assert.Fail(e.Message, e.StackTrace);
}
finally
{
transaction.Rollback();
}
}
}
}
IIdentifiable in my project is the most basic interface of my entities
the class is using the nunit.framework but u can do it with every testing framework u want
sessionfactoryprovider needs to supply the isession obj
here is a sample of use
/// <summary>
/// Testing the mapping of our entities.
/// there must be a server connection for this kind of test.
/// </summary>
[TestFixture]
internal class someMappingTest
{
[Test(Description = "Check the Encoding Profile FluentNHibernate Mapping")]
[Timeout(20000)]
public void checkthatMappingWorks()
{
// creatw the new entity
TestedType testOn = new TestedType();
// set the initialization values
testOn.Name = "TestProfileExecution";
// create the test object
new GenericMappingTesterWithRealDB<TestedType>
{
// assign an entity
EntityToTest = testOn,
// assign new values for update check
PerformEntityManipulationBeforeUpdate =
delegate(TestedType testedTypeBeingTested)
{
return testedTypeBeingTested.Name = "Updateing Test";
}
}.
// call run test to perform the mapping test.
RunTest();
}
}

Categories