dbcontext and reseting elements in memory - c#

I am trying to write some unit tests for some open source code.
one of the tests will test that only the minimal number of records have been loaded to memory
(ie if someone were to call:
DataContext.SomeTable.ToList().Where(s=>s.Id <=10)
the test should fail)
For this to work, DataContext.SomeTable.Load should be reset to have 0 items before the unit test executes.
At present, this TestFixture (using nunit, but that should not be relevant) is abstract, with the DbContext injected at instantiation, so that unit tests can test different providers.
I don't believe there is a way to clear the loaded entities, but was wondering how I might dispose and create a new DbContext which uses the same database provider as the injected context.

First, could you post your code?
Second, if I understand correctly, you would like to re-create the context every time? One solution is to pass in a function to create the context instead of the context itself. Like this:
public class MyTest {
private Func<IMyContext> createContext;
public MyTest(Func<IMyContext> createContext){
this.createContext = createContext;
}
[Test]
public void RunTest(){
using(var context = this.createContext()){
// do stuff with context
}
}
}

public class TestClass
{
private MyContext context;
[SetUp]
public void Setup()
{
// is executed before each test
context = new MyContext();
}
[Test]
public void Test1()
context.SomeTable.ToList().Where(s => s.Id <= 10);
}
[TearDown]
public void Complete()
{
context.Dispose();
}
}
each test should access table only once.
point of unit tests is that each test is for single scenario.

Related

Best practice for unit test cases

I am using the xUnit.net test framework and in each unit test I have certain steps which I am doing in each case. I would like to know if there is a way I call this method once before my unit case starts and also call when all unit test cases has been executed.
For example: In the scenario below I have two unit cases and in each case I am creating a local DB, populating it with data and then running my test and once it is done I am calling method to delete the DB. This I am doing in each test case. Instead of multiple creation I would like to create once and populate once and then delete db once all test case has been executed. It is important for me to delete what I have created as the test cases has certain cases which will fail if Database is not created when the tests are executed.
[Fact]
public void UnitCase1()
{
CreateDb();
UploadData();
...//My set of operation to test this case
...//Assert
DeleteDb()
}
[Fact]
public void UnitCase2()
{
CreateDb();
UploadData();
...//My set of operation to test this case
...//Assert
DeleteDb()
}
Editing after Answer from Eric:(I tried but its not working)
public class CosmosDataFixture : IDisposable
{
public static readonly string CosmosEndpoint = "https://localhost:8081";
public static readonly string EmulatorKey = "Mykey";
public static readonly string DatabaseId = "Databasename";
public static readonly string RecordingCollection = "collectionName";
string Root = Directory.GetParent( Directory.GetCurrentDirectory() ).Parent.Parent.FullName;
DocumentClient client = null;
public void ReadAllData( DocumentClient client )
{
//reading document code
}
public void ReadConfigAsync()
{
client = new DocumentClient( new Uri( CosmosEndpoint ), EmulatorKey,
new ConnectionPolicy
{
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp
} );
}
public void CreateDatabase()
{// create db code
}
private void DeleteDatabase()
{
// delete db code
}
public CosmosDataFixture()
{
ReadConfigAsync();
CreateDatabase();
ReadAllData( client );
}
public void Dispose()
{
DeleteDatabase();
}
}
public class CosmosDataTests : IClassFixture<CosmosDataFixture>
{
CosmosDataFixture fixture;
public CosmosDataTests( CosmosDataFixture fixture )
{
this.fixture = fixture;
}
[Fact]
public async Task CheckDatabaseandCollectionCreation()
{
List<string> collectionName = new List<string>();
var uri = UriFactory.CreateDatabaseUri(DatabaseId);// don't get DatabaseId or client says does not exist in current context
var collections = await client.ReadDocumentCollectionFeedAsync( uri );
foreach( var collection in collections )
{
collectionName.Add( collection.Id);
}
}
That's what [SetUp] and [TearDown] are for in NUnit. They are run right before and right after each test case, respectively. In xUnit you would usually implement a default constructor and IDisposable.
For example:
public TestClass()
{
CreateDb();
UploadData();
}
public void Dispose()
{
DeleteDb()
}
[Fact]
public void UnitCase1()
{
...//My set of operation to test this case
...//Assert
}
[Fact]
public void UnitCase2()
{
...//My set of operation to test this case
...//Assert
}
As other people have pointed out, such tests are in mainstream parlance not unit tests, but rather integration tests. xUnit.net is a fine framework for those kinds of tests, though, so apart from the semantic distinction, it makes little technical difference.
Apart from setting up the database in the test class' constructor and tearing it down in Dispose, as outlined by Eric Schaefer, you can also use xUnit.net's BeforeAfterTestAttribute. You'll then override Before to set up the database, and override After to tear it down:
public class UseDatabaseAttribute : BeforeAfterTestAttribute
{
public override void Before(MethodInfo methodUnderTest)
{
CreateDb();
UploadData();
base.Before(methodUnderTest);
}
public override void After(MethodInfo methodUnderTest)
{
base.After(methodUnderTest);
DeleteDb();
}
}
You can then annotate either each test method, or the entire test class with the attribute. I usually just annotate the class:
[UseDatabase]
public class DbTests
{
// Tests go here...
}
Since tests that use a database interact with a shared resource (the database), they can't easily run in parallel. By default, xUnit.net runs tests in parallel, so you may want to disable that. You can do it by adding an xunit.runner.json file:
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"parallelizeTestCollections": false
}
Finally, at least if you're using SQL Server, connection pooling will prevent you from deleting the database. You can either turn off connection pooling for your tests, or forcibly close other connections before teardown.
In my experience in Testing, I see 2 points here:
1-If you are checking that the data from the DB to another point in the program is being transmited correctly, that is Integration Testing, and it should be out of scope in the Unit Testing Plan, make sure that the responsabilities of a Unit Tester are clear where you work as there are some companies which avoid Integration Testing levels by assuming that if Functional Testing is 'OK', integrations should be too.
2- You mention at the end
It is important for me to delete what I have created as the test cases has certain cases which will fail if Database is not created when the tests are executed
but
I would like to create once and populate once and then delete db once all test case has been executed.
If I understand correctly, you need to do it for each Test Case as not all test cases are checking the same scenario, so it looks like those statements are the real problem here.
To answer your question, as it seems like you want to automate the process with minimum maintenance for the next releases, and I also know how the work environment tend to corner you to do some stuff that shouldn't be, I could think of a Preconditions Function and a Postcondition one, where you do it once and that's it.
If that is not possible for whatever reason, try to create another Test Case at the beginning (like Test Case 0) where you create and populate the DB (if apply, or separate it if needed) and another one at the end where you delete it.
I'm not familiar with the framework you are using, but I have a lot of experience in Testing, opening test levels and automating tasks, and hope that my answer could be of some help.

Is there a way to send a value from a unit test to another unit test?

I have a function which calculates some stuff and inputs it into a DB. This setup is important for all unit tests because they need some data to work on.
Sometimes i need to "flush" the DB, so all the unit tests point to the wrong ID.
Normally i just run the setup first and then change all the unit tests but this is taking to long tbh. Is there a way to automate this?
I would like to pass the generated ID into other unit tests.
So the idea was something like this:
[SetupFixture]
public class{
[Test]
public void SetupDB(){
setup();
//now marking the result somehow so other tests can pick the result up
return ID; //<--
}
}
public class OtherTests{
[Test]
[Get_ID_From_SetupDB]
public void testBaseOnID(int ID){
//we do stuff now with ID
}
}
PS: i have no problem switching the testing framework if you know a framework which can do this
Tests should be independent and you should generally never pass values between tests.
What you can do in your case, if all the tests are in the same class, is to have a variable in your class to hold the id and some global setup function that sets everything up and sets the variable to the correct id. In NUnit there is the [OneTimeSetUp] atribute for that.
[TestFixture]
public class MyTests
{
private int _testId;
[OneTimeSetUp]
public void SetItUp()
{
...
_testId = whatever;
}
[Test]
public void TestOne()
{
var whatever = _testId;
...
}
}

EntityFramework, DbContextScope and Effort - exception: DbContext has been disposed inside unit test

I am trying to write unit tests (with NUnit) for service layer which uses:
Entity Framework as Data Access Layer
DbContextScope for managing DbContext lifetime
I also use Effort.EF6 for mocking DbContext in unit tests. Unfortunately, I cannot find a way to make DbContextScope compatibile with Effort so that I can correctly test all cases.
Overview of the code
Service layer consists of classes (services) which do some business logic. Each method is treated as a complete transaction, ended with context.SaveChanges(). Example:
private IDbContextScopeFactory _dbContextScopeFactory;
public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory)
{
_dbContextScopeFactory = dbContextScopeFactory;
}
public BusinessModel.Department Insert(BusinessModel.Department department)
{
using (var dbContextScope = _dbContextScopeFactory.Create())
{
// Validation
ValidateAndThrowOnFailure(department, new DepartmentAddValidator());
// Operation
DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department);
newDepartment.InsertDateUTC = DateTime.UtcNow;
dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment);
dbContextScope.SaveChanges();
return Mapper.Map<BusinessModel.Department>(newDepartment);
}
}
To unit test such method, I do some preparation before each test:
private IDepartmentsService _departmentsService;
private IDbContextScopeFactory _dbContextScopeFactory;
private IDbContextFactory _dbContextFactory;
private DBModel.DPSContext _dbEntities;
[SetUp]
public void ReInitializeTest()
{
// Setup DbContext with Effort.EF6
string connStr = ConfigurationManager.ConnectionStrings["DPSContext"].ConnectionString;
DbConnection connection = EntityConnectionFactory.CreateTransient(connStr);
_dbEntities = new DBModel.DPSContext(connection);
// Fill DbContext with in-memory data
_dbEntities.Departments.AddRange(DataInitializer.GetDepartments());
_dbEntities.SaveChanges();
// Mock IDbContextFactory so that it returns in-memory context
var contextFactoryMock = new Mock<IDbContextFactory>();
contextFactoryMock
.Setup(f => f.CreateDbContext<DBModel.DPSContext>())
.Returns(_dbEntities);
_dbContextFactory = contextFactoryMock.Object;
// Setup DbContextScopeFactory to use mocked context
_dbContextScopeFactory = new DbContextScopeFactory(_dbContextFactory);
_departmentsService = new DepartmentsService(_dbContextScopeFactory);
}
The test and the problem
Here is a simple unit test:
[Test]
public void Insert_WhenValidModelPassed_ShouldInsertNewRecord()
{
// Given
BusinessModel.Department newDepartment = DataInitializer.GetExampleOfNewDepartment();
// When
_departmentsService.Insert(newDepartment);
// Then
Assert.AreEqual(3, _dbEntities.Departments.Count());
}
The problem is that the test fails with exception:
System.InvalidOperationException : The operation cannot be completed because the DbContext has been disposed.
It seems that DbContextScope used inside Insert method internally disposes assigned contexts at the end of the using block and thus the Assert throws exception when invoked. Has anybody come across a similar problem or just knows what I should do to successfuly test this and similar scenarios?
For anyone who comes across a similar issue, I've created a bit dirty-but-working solution (at least I hope so). In addition to what I wrote in the question, I created a class derived from real context and made Dispose method do... nothing. I also added a RealDispose method which is called at the end of each test.
public class TestableDPSContext : DBModel.DPSContext
{
public TestableDPSContext(DbConnection connection)
: base(connection)
{
}
protected override void Dispose(bool disposing)
{
// Do nothing
}
public void RealDispose(bool disposing)
{
// Invoke real dispose
base.Dispose(disposing);
}
}
[TearDown]
public void FinishTest()
{
_dbEntities.RealDispose(false);
}
Maybe there is a better solution, but for now it seems to resolve the problem with DbContext being disposed too early in tests.

How to prevent database effect in C# MVC Unit testing?

when I applying the unit testing on Insert, Update and Delete operation. At that time the record inserted, updated and deleted in the database as well.
How it possible?
Can anyone give the solution to prevent the database effect?
Thanks,
Ideally you should be using mocking for scenarios where db interaction is there. Make sure you have separate out your db interaction logic and it is interface driven. Then you can create mock objects, wherein you define the expectation of your db interaction interfaces. So for example, if for example InsertSomething() method is called, what should be returned from this method? and so on. Sharing some links on details about unit testing and mocking.
https://msdn.microsoft.com/en-us/library/ff650441.aspx
https://github.com/Moq/moq4
http://www.developerhandbook.com/unit-testing/writing-unit-tests-with-nunit-and-moq/
Testing a MVC Controller fails with NULL reference exception
As another option, you can go for separate real database for testing purpose, if it is essential to execute tests against database. You can also, execute sql script at the start and after running the test to seed and clean data respectively to keep the database pristine
It is possible either by mocking your database (used in unit tests) or creating new database used only for testing purpose (integration tests).
I would recommend using these two approaches together. Remember, that amount of unit tests should be much bigger than integration tests.
Here is simple example how to mock database (using Moq library).
public class HomeController : Controller
{
private IUserRepository _repository;
public HomeController(IUserRepository repository)
{
_repository = repository;
}
public ActionResult AddNewUser(User newUser)
{
_repository.AddUser(newUser);
return View();
}
}
public interface IUserRepository
{
void AddUser(User newUser);
}
public class UserRepository : IUserRepository
{
private DBContext _context;
public UserRepository(DBContext context)
{
_context = context;
}
public void AddUser(User newUser)
{
_context.Users.Add(newUser);
}
}
[Test]
public void ShouldMockRepository()
{
// Given
var repository = new Mock<IUserRepository>();
var controller = new HomeController(repository.Object);
// When
controller.AddNewUser(new User());
// Then
repository.Verify(r => r.AddUser(It.IsAny<User>()), Times.Once);
}

MS test + EF (VS2008): when running all tests: errors about different ObjectContexts

In Visual Studio 2008, C#, Entity Framework 3.5, MS Test, I have two tests:
[TestMethod]
public void Test1()
{
using (Entities context = new Entities())
{
using (TransactionHelper transaction = new TransactionHelper(context))
{
// Stuff
transaction.Rollback();
}
}
}
And:
[TestMethod]
public void Test2()
{
using (Entities context = new Entities())
{
using (TransactionHelper transaction = new TransactionHelper(context))
{
// Stuff
transaction.Rollback();
}
}
}
When running them individually, they both pass. But when I run all tests, it says the following for the second test:
System.InvalidOperationException: The relationship between the two
objects cannot be defined because they are attached to different
ObjectContext objects..
I do not understand this at all. Nowhere in the codepath do I create a new Entities object. I checked several times (and seeing as how the code works in indivual test runs and production use, that must be true). And I even wrapped the Entities in a using statement.
Is there some weird retention in the TestContext perhaps?
For reference, the TransactionHelper (simplified):
public class TransactionHelper : IDisposable
{
private global::System.Data.Objects.ObjectContext _context;
private ConnectionState _initialConState;
private DbTransaction _transaction;
public TransactionHelper(global::System.Data.Objects.ObjectContext context)
{
_context = context;
_initialConState = context.Connection.State;
if (_initialConState != ConnectionState.Open)
{
_context.Connection.Open(); // open connection if not already open
}
_transaction = _context.Connection.BeginTransaction(IsolationLevel.ReadUncommitted);
}
public void Rollback()
{
_transaction.Rollback();
}
public void Commit()
{
_transaction.Commit();
}
public void Dispose()
{
_transaction.Dispose();
if (_initialConState != ConnectionState.Open)
{
_context.Connection.Close(); // only close connection if not initially open
}
}
}
It makes no difference if I use isolation level ReadCommitted.
Since unit tests should ideally be totally independent of each other (in terms of state, order, etc), Visual Studio and other test frameworks such as NUnit speed up the execution process by running them in parallel. Shared static resources usually turned out to be the culprit for me in cases like yours.
It might be that these simple objects you mention (e.g some lookup values) get assigned to a property of an entity object, and then in the second test you assign the same simple object to an entity of another context, which would raise an error like you mentioned.
If this is the case then you might try mocking the cache in your tests (or maybe minimize the use of static).

Categories

Resources