I am trying to use Integration testing using EF 6.1 and run into a problem that my migration configuration settings are used where I dont need them. And I cant figure out how to swap them out for testing.
Here is my Test Class:
[TestClass]
public class SXSeasonConverterTests
{
public void RecreateDatabaseForTesting()
{
Database.SetInitializer(new TestDatabaseSeedingInitializer());
using (var context = new BaseNFLContext("NFLContextIntegrationTests"))
{
context.Database.Initialize(true);
}
}
public SXSeasonConverterTests()
{
RecreateDatabaseForTesting();
}
}
Here is my Initializer class:
public class TestDatabaseSeedingInitializer : DropCreateDatabaseAlways<BaseNFLContext>
{
protected override void Seed(BaseNFLContext context)
{
//Add Teams
context.Teams.Add(new Team { Code = "ARZ", Name = "Arizona Cardinals" });
context.Teams.Add(new Team { Code = "ATL", Name = "Atlanta Falcons" });
...
}
}
However when I try to run the test, I get the error that my AutomaticMigrations are disabled. When I looked further I found that It uses this code on Initialize:
internal sealed class NFLConfiguration : DbMigrationsConfiguration<BaseNFLContext>
{
public NFLConfiguration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
}
This code is obviously there for production. However when doing testing how can I swap those migration configurations and set AutomaticMigrationsEnabled = true;?
I used to test my EF stuff using a special unittesting database and executed the tests in a TransactionScope which was rolled back at the end of the test. This way, no data was actually stored in the database.
I wasn't fast, but it suited our purpose.
You should create a separate project for testing and have a separate Db context that points to a test database. You can create something like a IDbContext interface that tells you which object models need to be tested. Also, the data access layer needs to allow you to inject this test Db context as a dependency.
Related
I seem to have a strange concurrency issue I can't seem to put my finger on.
When I construct my implementation of a DbContext I inject the Entities I want to be built by the modelbuilder (don't worry about why). This is only done once by my app at runtime and runs fine, but when I'm integration testing DB integration, I inject only the test entities I need for my InMemoryDatabase.
Now I seem to have a weird issue where two unit tests in different class files needing different entities seem to get crossed over.
I keep running the unit tests and the first test will pass, but the second test will fail saying that TestObjectB doesn't exist in the model. When I inspect the model it says TestObjectA exists instead, even though it wasn't injected on this test. So as if the implementation of the DataContext was static and overwritten...These are different files and fresh constructors for the context, I don't understand how they are crossing paths? If I run the unit test that fails on it's own, it passes.
Note the following code has been simplified for your view.
DB Context:
public class DataContext : DbContext
{
private readonly List<IEntity> _entities;
public DataContextA(List<IEntity> entities, DbContextOptions options) : base(options)
{
_entities = entities;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in _entities)
{
modelBuilder.Entity(entity.GetType());
}
}
}
Test Implementation 1:
[Fact]
public void CheckUniqueFieldA()
{
var options = new DbContextOptionsBuilder<DataContext>();
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
using (var context = new DataContext(new List<IEntity> { new TestObjectA() }, options.Options))
{
//Do Something
}
}
Test Implementation2:
[Fact]
public void CheckUniqueFieldB()
{
var options = new DbContextOptionsBuilder<DataContext>();
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
using (var context = new DataContext(new List<IEntity> { new TestObjectB() }, options.Options))
{
//Do Something
}
}
The reason is EF Core model caching described in Alternating between multiple models with the same DbContext type documentation topic:
...the model caching mechanism EF uses to improve the performance by only invoking OnModelCreating once and caching the model.
By default EF assumes that for any given context type the model will be the same.
The link also contains an example how to solve it. You'd need to create custom implementation of IModelCacheKeyFactory interface and replace the default EF Core implementation using ReplaceService inside OnConfiguring. The implementation should return an object representing the unique cache key for a given DbContext instance. The default implementation simply returns context.GetType().
I trying to test some code that uses Entity Framework, but I can't figure out how to reference the EF Context classes from the separate MSTest project. Both projects are in the same solution.
Cannot convert lambda expression to type 'DbContextOptions' because it is not a delegate type
In my Test case:
[TestClass]
public class GreenCardUserTest
{
[TestMethod]
public void TestAddUser()
{
// REFERENCE TO OTHER PROJECT. WORKS FINE
AppUserViewModel a = new AppUserViewModel();
//LIKELY INCORRECT attempt to duplicate code from Startup.cs in other project
using (GreenCardContext _gc = new GreenCardContext(options => options.UseSqlServer(Configuration.GetConnectionString("MyConnection"))))
{
new GCLandingUserModel().AddUser(a,_gc);
}
}
}
Excerpt from main project Startup.cs (which works fine):
services.AddDbContext<GreenCardContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConnection")));
I would suggest using InMemoryDatabase. In your test class, use [TestInitialize] to setup your dummy database:
[TestClass]
public class GreenCardUserTest
{
private readonly context;
[TestInitialize]
public Setup()
{
DbContextOptions<GreenCardContext> options;
var builder = new DbContextOptionsBuilder<GreenCardContext>();
builder.UseInMemoryDatabase();
var options = builder.Options;
context = new GreenCardContext(options);
}
[TestMethod]
public void TestAddUser()
{
// user context here...
}
}
What you have to do is:
1) Add a reference in your test project to your context's project (if you haven't already)
2) Add references to Entity Framework to your test project
3) Add an appconfig to your test project and set entity framework config on it. Your test will read the configuration from it's own config, not your app's. Very useful as you can, by example, use dblocal and codefirst in tests and sqlserver when on running :)
You have done some of this, I think the point you are missing is the third :)
The code that you have from Startup.cs is using a delegate to tell your application how to build your DbContext at runtime.
However in your test, you need to actually provide an instance of DbContextOptions, not just a delegate. To do this, you can use DbContextOptionsBuilder:
var options = new DbContextOptionsBuilder<GreenCardContext>()
.UseSqlServer(Configuration.GetConnectionString("MyConnection"))
.Options;
using (GreenCardContext _gc = new GreenCardContext(options))
{
new GCLandingUserModel().AddUser(a,_gc);
}
Also, if you do insist on unit testing your DbConext, you may want to look into using InMemoryDatabase so that you don't need an open SQL connection in your tests. See this document for more details.
I'm using Entity Framework in ASP.net Core, I have many classes where I am using an instance of the DbContext to interact with the database, I'm using constructor injection in every class and controller but I feel like a trained monkey repeating that code all the time, maybe I'm doing this the wrong way and there are better ways to do this. I'm feeling tired to write again and again the same code for every controller and every class I'm using an instance of DbContext..
Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<DBConnection> Configuration.GetSection("ConnectionStrings"));
}
That code is fine, no problem at all, but now: Do I need to repeat the code below for every class and controller?? I know I have not read a lot yet about services and that I might be losing something obvious, I'm just copying the constructors, but in some classes where I need instances I need to add a field named _connectionAccessor of type IOptions<DBConnection>:
public FirstController(IOptions<DBConnection> connectionsAccessor)
{
_context = new DataContext(connectionsAccessor);
_connectionAccessor = connectionsAccessor;
}
public EmailController(IOptions<DBConnection> connectionsAccessor)
{
this.ctx = new DataContext(connectionsAccessor);
_connectionAccessor = connectionsAccessor;
}
public MyRepository(IOptions<DBConnection> connectionsAccessor)
{
_context = new DataContext(connectionsAccessor);
_connectionAccessor = connectionsAccessor;
}
And like that for every controller and class, and I have a lot of classes and controllers, is this the only way?
Oh this is my DbContext class:
public class DataContext : DbContext
{
public DataContext(IOptions<DBConnection> connectionsAccessor)
{
this.Database.Connection.ConnectionString = connectionsAccessor.Value.MySQLContext;
}
I have a repository implemented using LINQ to SQL. I need to do Unit Testing though I don't have a database. How can I write the UT for FreezeAllAccountsForUser method? Can you please show an example using manually mocking?
Note: There is inheritance mapping used in domain objects
Note: Unit Testing is to be done using Visual Studio Team Test
Comment from #StuperUser. Unit testing involves completely isolating code from the other objects it interacts with. This means that if the code fails, you can be sure that the failure is to do with the code under test. To do this you have to fake these objects.
CODE
public void FreezeAllAccountsForUser(int userId)
{
List<DTOLayer.BankAccountDTOForStatus> bankAccountDTOList = new List<DTOLayer.BankAccountDTOForStatus>();
IEnumerable<DBML_Project.BankAccount> accounts = AccountRepository.GetAllAccountsForUser(userId);
foreach (DBML_Project.BankAccount acc in accounts)
{
string typeResult = Convert.ToString(acc.GetType());
string baseValue = Convert.ToString(typeof(DBML_Project.BankAccount));
if (String.Equals(typeResult, baseValue))
{
throw new Exception("Not correct derived type");
}
acc.Freeze();
DTOLayer.BankAccountDTOForStatus presentAccount = new DTOLayer.BankAccountDTOForStatus();
presentAccount.BankAccountID = acc.BankAccountID;
presentAccount.Status = acc.Status;
bankAccountDTOList.Add(presentAccount);
}
IEnumerable<System.Xml.Linq.XElement> el = bankAccountDTOList.Select(x =>
new System.Xml.Linq.XElement("BankAccountDTOForStatus",
new System.Xml.Linq.XElement("BankAccountID", x.BankAccountID),
new System.Xml.Linq.XElement("Status", x.Status)
));
System.Xml.Linq.XElement root = new System.Xml.Linq.XElement("root", el);
//AccountRepository.UpdateBankAccountUsingParseXML_SP(root);
AccountRepository.Update();
}
Repository Layer
namespace RepositoryLayer
{
public interface ILijosBankRepository
{
System.Data.Linq.DataContext Context { get; set; }
List<DBML_Project.BankAccount> GetAllAccountsForUser(int userID);
void Update();
}
public class LijosSimpleBankRepository : ILijosBankRepository
{
public System.Data.Linq.DataContext Context
{
get;
set;
}
public List<DBML_Project.BankAccount> GetAllAccountsForUser(int userID)
{
IQueryable<DBML_Project.BankAccount> queryResultEntities = Context.GetTable<DBML_Project.BankAccount>().Where(p => p.AccountOwnerID == userID);
return queryResultEntities.ToList();
}
public virtual void Update()
{
//Context.SubmitChanges();
}
}
}
Domain Classes
namespace DBML_Project
{
public partial class BankAccount
{
//Define the domain behaviors
public virtual void Freeze()
{
//Do nothing
}
}
public class FixedBankAccount : BankAccount
{
public override void Freeze()
{
this.Status = "FrozenFA";
}
}
public class SavingsBankAccount : BankAccount
{
public override void Freeze()
{
this.Status = "FrozenSB";
}
}
}
Auto generated Class by LINQ to SQL
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.BankAccount")]
[InheritanceMapping(Code = "Fixed", Type = typeof(FixedBankAccount), IsDefault = true)]
[InheritanceMapping(Code = "Savings", Type = typeof(SavingsBankAccount))]
public partial class BankAccount : INotifyPropertyChanging, INotifyPropertyChanged
Simply, you can't. The sole purpose of the repository implementation is talking to the database. So the database technology does matter and you should perform integration tests.
Unit testing this code is impossible because LINQ to Objects is a superset of LINQ to SQL. You might have a green unit test and still get the runtime exception when using real db because you used a feature of LINQ in your repository that cannot be translated into SQL.
The Repository responsibility is to persist domain objects and fetch them on request. i.e. it's job is to take an object and deserialize/serialize it to from some form of durable storage.
So tests for the repository have to test against the real storage in this case a DB. i.e. these are integration tests - tests that your class integrates with the external DB.
Once you have this nailed, the rest of the client/app doesn't have to work against the real DB. They can mock the repository and have fast unit tests. You can assume that GetAccount works since the integration tests pass.
More details:
By passing in the Repository object as a ctor or method arg, you open the doors for passing in a fake or a mock. Thus now the service tests can run without a real repository >> there is no DB-access >> fast tests.
public void FreezeAllAccountsForUser(int userId, ILijosBankRepository accountRepository)
{
// your code as before
}
test ()
{ var mockRepository = new Mock<ILijosBankRepository>();
var service = // create object containing FreezeAllAccounts...
service.FreezeAllAccounts(SOME_USER_ID, mockRepository);
mock.Verify(r => r.GetAllAccountsForUser(SOME_USER_ID);
mock.Verify(r => r.Update());
}
You can by using the IDbSet interface in your datacontext and extracting an interface for your datacontext class. Programming towards interfaces is the key to creating unit testable code.
The reason that you would want to create unit tests for these linq queries is to have a unit test for the logical query. Integration tests are subject to all kinds of false negatives. The db not being in the right state, other queries running at the same time, other integration tests, etc. It is very hard to isolate a db well enough for a reliable integration test. That is why integration tests are so often ignored. If i have to pick one, i want the unit test...
I'm using Entity Frameworc 4.1 Code First and Moq. And I want to test database initializer. Also I have the abstract BaseUnitOfWork class which inherited from DbContext (so, for testing it should be mocked).
public abstract class BaseUnitOfWork : DbContext, IUnitOfWork
{
...
public IDbSet<User> Users
{
get
{
return Set<User>();
}
}
...
}
User is simple POCO with three properties: Id, Login, Password.
And here is the code of the DbInitializer:
public class BaseDbInitializer : DropCreateDatabaseAlways<BaseUnitOfWork>
{
protected override void Seed(BaseUnitOfWork context)
{
base.Seed(context);
context.Set<User>().Add(new User { Login = "admin", Password = "1" });
context.SaveChanges();
}
}
I'm trying to test this initializer with the next test (NUnit is used):
[TestFixture]
public class BaseDbInitializerTests
{
private BaseUnitOfWork _baseUnitOfWork;
[TestFixtureSetUp]
public void Init()
{
Database.SetInitializer(new BaseDbInitializer());
_baseUnitOfWork = new Mock<BaseUnitOfWork>(Consts.ConnectionStringName).Object;
_baseUnitOfWork.Database.Initialize(true);
}
[TestFixtureTearDown]
public void CleanUp()
{
_baseUnitOfWork.Dispose();
Database.Delete(Consts.ConnectionStringName);
}
[Test]
public void ShouldInitializeBaseDb()
{
var repository = new Mock<BaseRepository<User>>(_baseUnitOfWork).Object;
var firstUserInDb = repository.FindBy(x => x.Login == "admin" && x.Password == "1").SingleOrDefault();
Assert.That(firstUserInDb, Is.Not.Null);
Assert.That(firstUserInDb.Login, Is.EqualTo("admin"));
Assert.That(firstUserInDb.Password, Is.EqualTo("1"));
}
}
Unfortunately it seems like Seed method of the BaseDbInitializer class doesn't execute. DB is recreating, but there is no any records, and I tried to debug this test and Seed method was executed during debug session.
The strategy DropCreateDatabaseAlways<BaseUnitOfWork> is looking for an exact type match of BaseUnitOfWork, not a derived type, and not Mock<BaseUnitOfWork>. If you need it, you'll have to implement a copy of the strategy for the mocked type.
What is the point of mocking context and in the same time expecting database to exists? The point of mocking is removing dependency on the database (but it will not always work as expected).
So either use mock (unit test with all its problems) or database with real context and integration test.