MSTest fails when I do run all, but works otherwise - entity-framework

So I have a Testclass using MSTest and every test works great if I run them one and one, however if I select 2 tests, namely can_register and cannot_Register_existing_username then the second fails (cannot_register_existing_username).
I have let my testclass inherit from an abstract class that looks like this:
public abstract class RollbackCapabilities
{
private TransactionScope _transactionScope;
[TestInitialize]
public virtual void TestInitialize()
{
_transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions { Timeout = new TimeSpan(0, 10, 0) });
}
[TestCleanup]
public virtual void TestCleanup()
{
Transaction.Current.Rollback();
_transactionScope.Dispose();
}
}
If I comment this file out then it works (but now the data remains in the test-db which I don't want).
With this file above active the second test fails, the tests look like this
[TestMethod]
public void Can_Register()
{
//Arrange
AccountController ac = ControllerFactory.CreateAccountController();
RegisterModel model = new RegisterModel();
model.UserName = "TestUser";
model.Password= "TestPassword";
model.ConfirmPassword = "TestPassword";
//Act
ActionResult result = ac.Register(model);
//Assert
Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
Assert.AreEqual("Home", ((RedirectToRouteResult)result).RouteValues["controller"]);
Assert.AreEqual("Index", ((RedirectToRouteResult)result).RouteValues["action"]);
}
[TestMethod]
public void Cannot_Register_Existing_Username()
{
//Arrange
AccountController ac = ControllerFactory.CreateAccountController();
RegisterModel model = new RegisterModel();
model.UserName = "TestUser";
model.Password = "TestPassword";
model.ConfirmPassword = "TestPassword";
ac.Register(model);
RegisterModel model2 = new RegisterModel();
model2.UserName = "TestUser";
model2.Password = "OtherTestPassword";
model2.ConfirmPassword = "OtherTestPassword";
//Act
ActionResult result = ac.Register(model2);
//Assert
Assert.IsInstanceOfType(result, typeof(ViewResult));
Assert.AreEqual("", ((ViewResult)result).ViewName);
Assert.AreEqual(model2, ((ViewResult)result).ViewData.Model);
}
and finally the error i get is as follows:
Test method
Viducate.UnitTests.UserHandling.RegisterTests.Cannot_Register_Existing_Username
threw exception: System.Data.EntityCommandExecutionException: An
error occurred while executing the command definition. See the inner
exception for details. ---> System.Data.SqlClient.SqlException:
Invalid object name 'dbo.Users'.
Thats my problem, not big but very annoying and as mentioned if I run the tests one and one it works, it also works but leaves data in the db if I comment out my RollbackCapabilities class

Okay so I found out that my error was that I had created the database (but not tables) by hand because create database is not supported in multi-transaction.
however creating an empty database means that EF assumes there is tables already and that is why it failed with dont know what dbo.users are.
So what I did was created the tables as well and now it works. However this means I can never run this on a new development machine without first creating the tables and database. so annoying.
I think I will set up another test class that does not inherit my abstract rollback class and hade that create the tables permanently... should solve the problem as long as that runs first.

Related

SpringbootTest + TestContainers: how do I refresh the database after tests pollute the database

I am using an abstract class like this:
#SpringBootTest(classes = MyAppApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public abstract class AbstractIntegrationTest {
static {
PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer().withPassword("password")
.withUsername("postgres").withDatabaseName("MyApp");
postgreSQLContainer.start();
System.setProperty("spring.datasource.url", postgreSQLContainer.getJdbcUrl());
System.setProperty("spring.datasource.password", postgreSQLContainer.getPassword());
System.setProperty("spring.datasource.username", postgreSQLContainer.getUsername());
}
Then I have many tests that leverage that use that class like this:
public class moreTests extends AbstractIntegrationTest {
TestRestTemplate restTemplate = new TestRestTemplate("my-user", "password");
HttpHeaders headers = new HttpHeaders();
#Test
public void SimpleHealthCheck() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/api/v1/healthcheck"),
HttpMethod.GET, entity, String.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
#Test
public void GetInst() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/api/v1/institutions"),
HttpMethod.GET, entity, String.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
However, some of my tests will pollute the database. I'd like to control if a test runs with a fresh database or not. What's the prescribed way to do this?
After more reading about Spring boot integration testing, it appears the prescribed way is to use the "#DirtiesContext" annotation for tests that are destructive (or dirty).
EDIT: After a few months, I realized #DirtiesContext is not awesome. It basically resets the whole app which can be expensive. Also, #DirtiesContext May not reset your database in some cases depending on how your app works. I suggest having a cleanup SQL script that runs in your #BeforeAll or #AfterAll section of each test class. This cleanup SQL script needs to be carefully written.
you either use the #Before annotation to clean everything before executing your tests.
Or you clean in each test before you execute.
Each test should be independent from the other. So usually:
clear and set up expectations
run test
If test fails, your database will be in the failed state so you can check what happened.

I have "Roach Motel Data" - data go into DB fine, cannot get back out

I'm using Xamarin.Forms with EF and SqLite. I've installed the "Microsoft.EntityFrameworkCore.Sqlite" Nuget package in my project. The code issue is in the shared code project, .NetStandard 2.0.
I have created a simple class, let's say CAT class to hold my DB table objects
I can use the "ensurecreated" command and that works fine
I can create a CAT, set properties and SaveChanges() to the DB; this works fine, I can see the data in the DB
I cannot get the data back out; I get an "object not set to a reference..." error.
Ignore my couple of outer curly braces; new to posting code and only way to get it all together in one block. I have handled the platform-specific (Android & iOS) code for obtaining the dbPath to the SqLite .db3 file (not shown here).
Cannot figure what I'm missing that no data will come back out of DB. Any help much appreciated!
{
public class DatabaseContext : DbContext
{
string _dbPath;
public DbSet<Cat> Cats { get; set; }
public DatabaseContext(string dbPath)
{
_dbPath = dbPath;
Database.EnsureCreatedAsync();
}
public async Task<IEnumerable<Cat>> GetCats()
{
var allCats = await Cats.ToListAsync().ConfigureAwait(false);
return allCats;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Filename={_dbPath}");
}
}
List<Cat> itemSource;
// Create Database & Tables
using (var db = new DatabaseContext(App.dbPath))
{
// Ensure database is created
db.Database.EnsureCreated();
// Insert Data
db.Add(new Cat() { IdCat = "111", Fname = "Felix1" });
db.SaveChanges();
// Retreive Data
//method 1
// RESULT: no data are in "itemsource", info reads "exception count = 1"
itemSource = db.Cats.ToList();
// method 2
// RESULTS: crashes with error "System.NullReferenceException: Object reference not set to an instance of an object."
Task<IEnumerable<Cat>> p = db.GetCats();
itemSource = db.Cats.ToList();
}
}

Unit testing With Entity Framework 7, Test fails sometimes?

I have a bunch of test where I use the new UseInMemory function in EF7. When I run them all some of them fail. When I run them single they all pass.
My best guess it is a conflict in EF7 because of the fact that every test runs in its own thread and they all kind of using the same DbContext class.
Here one of my Tests:
[Fact]
public void Index()
{
DbContextOptionsBuilder<DatabaseContext> optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
optionsBuilder.UseInMemoryDatabase();
db = new DatabaseContext(optionsBuilder.Options);
AdminController controller = new AdminController(db);
var result = controller.Index() as ViewResult;
Assert.Equal("Index", result.ViewName);
}
I remake the dbContext object in every test but it seem not to make any different.
Would be greatful for any input. Thanks :)
The problem is, that the memory storage in InMemoryDatabase is registered as Singleton so you actually share the data between DbContexts even you think you don't.
You have to create your DbContexts like this:
public abstract class UnitTestsBase
{
protected static T GetNewDbContext<T>() where T : DbContext
{
var services = new ServiceCollection();
services
.AddEntityFramework()
.AddInMemoryDatabase()
.AddDbContext<T>(options => options.UseInMemoryDatabase());
var serviceProvider = services.BuildServiceProvider();
var dbContext = serviceProvider.GetRequiredService<T>();
dbContext.Database.EnsureDeleted();
return dbContext;
}
}
var newTestDbContext = GetNewDbContext<TestDbContext>()
I also was led to beleive that .UseInMemoryDatabase() has no persistence, but that does not seem to be the case (at least with the latest versions)!
As noted in How can I reset an EF7 InMemory provider between unit tests? you want to do a db.Database.EnsureDeleted() BUT I also noticed that this does NOT reset auto increment ids.

Testing EF ConcurrencyCheck

I have a base object, that contains a Version property, marked as ConcurrencyCheck
public class EntityBase : IEntity, IConcurrencyEnabled
{
public int Id { get; set; }
[ConcurrencyCheck]
[Timestamp]
public byte[] Version { get; set; }
}
This works, however, I want to write a test to ensure it doesn't get broken. Unfortunately, I can't seem to figure out how to write a test that doesn't rely on the physical database!
And the relevant test code that works, but uses the database...
protected override void Arrange()
{
const string asUser = "ConcurrencyTest1"; // used to anchor and lookup this test record in the db
Context1 = new MyDbContext();
Context2 = new MyDbContext();
Repository1 = new Repository<FooBar>(Context1);
Repository2 = new Repository<FooBar>(Context2);
UnitOfWork1 = new UnitOfWork(Context1);
UnitOfWork2 = new UnitOfWork(Context2);
Sut = Repository1.Find(x => x.CreatedBy.Equals(asUser)).FirstOrDefault();
if (Sut == null)
{
Sut = new FooBar
{
Name = "Concurrency Test"
};
Repository1.Insert(Sut);
UnitOfWork1.SaveChanges(asUser);
}
ItemId = Sut.Id;
}
protected override void Act()
{
_action = () =>
{
var item1 = Repository1.FindById(ItemId);
var item2 = Repository2.FindById(ItemId);
item1.Name = string.Format("Changed # {0}", DateTime.Now);
UnitOfWork1.SaveChanges("test1");
item2.Name = string.Format("Conflicting Change # {0}", DateTime.Now);
UnitOfWork2.SaveChanges("test2"); //Should throw DbUpdateConcurrencyException
};
}
[TestMethod]
[ExpectedException(typeof(DbUpdateConcurrencyException))]
public void Assert()
{
_action();
}
How can I remove the DB requirement???
I would recommend extracting your MyDbContext into an interface IMyDbContext, and then creating a TestDbContext class that will also implement SaveChanges the way you have it up there, except with returning a random value (like 1) instead of actually saving to the database.
At that point then all you'd need to do is to test that, in fact, all of the entities got their version number upped.
Or you could also do the examples found here or here, as well.
EDIT: I actually just found a direct example with using TimeStamp for concurrency checks on this blog post.
It's my opinion that you should not try to mock this behaviour to enable "pure" unit testing. For two reasons:
it requires quite a lot of code that mocks database behaviour: materializing objects in a way that they have a version value, caching the original objects (to mock a store), modifying the version value when updating, comparing the version values with the original ones, throwing an exception when a version is different, and maybe more. All this code is potentially subject to bugs and, worse, may differ slightly from what happens in reality.
you'll get trapped in circular reasoning: you write code specifically for unit tests and then... you write unit tests to test this code. Green tests say everything is OK, but essential parts of application code are not covered.
This is only one of the many aspects of linq to entities that are hard (impossible) to mock. I am compiling a list of these differences here.

Is this safe? - NUnit base class opens and rollsback a TransactionScope

I was thinking it would be nice to create a base class for NUnit test fixtures that opens a TransactionScope during the SetUp phase, then rolls back the transaction during tear down.
Something like this:
public abstract class TestFixtureBase
{
private TransactionScope _transaction;
[TestFixtureSetUp]
public void TestFixtureSetup()
{
_transaction = new TransactionScope();
}
[TestFixtureTearDown]
public void TestFixtureTearDown()
{
if (_transaction != null)
{
_transaction.Dispose();
}
}
}
Do you think this is a good idea?
Obviously the database is just a test database, not a live database, but it would still be annoying if it filled up with junk data from the unit tests.
What do other people do when running unit tests that involve a lot of data access?
You want to be careful here. TransactionScope is going to promote the transaction to a distributed transaction if you open up more than one connection to the database. I find that it is easier just to write some simple SQL that clears out the tables of interest to my test class before I start running the test.
EDIT: Normally I would call any test that touches the database an integration test since it involves another system. Typically, I will mock out the database when unit testing my code.
[TestSetup]
public void Setup()
{
foreach (string table in new string[] { "table1", "table2" })
{
ClearTable( table );
}
}
private void ClearTable( string table )
{
...standard stuff to set up connection...
SqlCommand command = connection.CreateCommand() );
command.CommandText = "delete from " + table;
command.ExecuteNonQuery();
... stuff to clean up connection...
}
I've used XtUnit
It automatically rolls back at the end of a unit test. You can simply add a [Rollback] attribute to the test. It's an extension to NUnit or MbUnit