When unit testing with NHibernate I will typically have tests that create and save an object, clear the session (session.Clear()) then retrieve the object from the database.
What's the equivalent of Session.Clear() with EF4?
Example test:
[Test]
public void Can_create_and_save_a_default_account()
{
var account = new Account();
_db.Accounts.AddObject(account);
_db.SaveChanges();
int id = account.AccountId;
// clear session
var fromDb = _db.Accounts.SingleOrDefault(x => x.AccountId == id);
Assert.IsNotNull(fromDb);
}
That will be recreating your DataContext-derived class (_db in your case).
You could mock your remote database with in-memory database. Here is example
SO after each test you will start from scratch.
Related
I have a controller with two parameters and need to test them via unit tests. Want to test 4 parameters, ViewBug, etc. But how I can make fake DB context and logger? I'm stuck at this moment:
[Fact]
public void IndexReturnsAViewResultWithAListOfUsers()
{
// Arrange
var mock = new Mock<AircraftsController>();
var controller = new AircraftsController(/*params*/);
// Act
// Assert
}
This is my controller:
public class AircraftsController : Controller
{
#region DbContext, Logger
public AppDbContext Context { get; }
private readonly ILogger<AircraftsController> _logger;
public AircraftsController(AppDbContext context, ILogger<AircraftsController> logger)
{
Context = context;
_logger = logger;
_logger.LogDebug(1, "NLog injected into Controller");
}
#endregion
[HttpGet]
public IActionResult Compare(int vehicle1, int vehicle2, int vehicle3, int vehicle4)
{
var planesFromDb = Context.Planes.OrderBy(x => x.BR).ToList();
planesFromDb.Insert(0, new Plane { Image = "~/images/EmptyPlane.png", Nation = "EmptyFlag", Name = "Select aircraft", VehicleId=0 });
var selectedPlanes = new List<Plane>();
ViewBag.AllPlanesSelected = planesFromDb;
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle1));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle2));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle3));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle4));
_logger.LogInformation("Log Message");
return View(selectedPlanes);
}
}
As Stephen has suggested the in-memory provider is a good option for mocking an EFCore db context. It works for most things.
I have a requirement to use Moq for 3rd party dependencies so I'd create mocks for both. For the db context I'd use EntityFrameworkCore.Testing (disclaimer, I am the author):
var mockedDbContext = Create.MockedDbContextFor<AppDbContext>();
Then for the logger I'd create a mock using Mock.Of
var mockedLogger = Mock.Of<ILogger<AircraftsController>>();
Easy one liners that you can then use to create your controller in your unit test. Overall I am an advocate of using the EFCore in-memory provider if it suits the unit test. Using mocks does have other advantages such as allowing you to verify invocations.
In .NET Core, you can take advantage of in-memory databases for unit tests. There are two options, EF In-Memory database, and SQLite In-Memory database. I prefer SQLite In-Memory because it gives you all the advantages of handling relational data, unlike EF In-Memory.
Testing with the EF In-Memory Database
SQLite In-Memory Database
Below is a simple implementation for unit tests using SQLite In-Memory database:
public YourContext GetDbContext()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var option = new DbContextOptionsBuilder<YourContext>()
.UseSqlite(connection,
s => {
s.UseNetTopologySuite();
s.MigrationsHistoryTable("__MigrationHistory");
}).Options;
var dbContext = new YourContext(option);
//Added to recreate database and run migration for each test.
if (dbContext != null)
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
}
return dbContext;
}
And then in unit test:
var context = GetDbContext();
Alternatively, you can place the GetDbContext method in a fixture, so that you are only re-creating the database one time per test class. Have a dispose method in the fixture to run the dbContext.Database.EnsureDeleted() to clean up the data between test classes.
Shared context between Tests - Class Fixtures
I try to make a unit test with real database (mean not in-memory database).
this is my config file:
#TestConfiguration
#EnableJpaAuditing
public class TestConfig {
#Bean
#Primary
public DataSource getDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/faptv");
hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariDataSource.setUsername("mansion");
hikariDataSource.setPassword("mansion");
return hikariDataSource;
}
}
This is my sample test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#ContextConfiguration(classes = {TestConfig.class})
#DirtiesContext
#Transactional
public class BaseEntityTest {
#Autowired
private TBL120Dao dao;
#Test
#Sql("classpath:/sql/abc.sql")
public void test() {
TBL120Dto dto = new TBL120Dto();
dto.setUserId("xxx");
dto.setUsername("ccsc");
dto.setPasswordHash("passss");
dao.save(dto);
assertThat(dao.findByUserId("kkk").isPresent()).isTrue();
}
}
This is my /sql/abc.sql
insert into tbl120(user_id, password_hash, username) value ("kkk", "ssss", "Ssss");
When the test is finished:
I see in my real database: it's has new row from sql.
But it don't have the row with userId 'xxx' from the test method. And, when I debug inside test method, It only exist once record of userId 'xxx' when I try to execute dao.findAll() (note that in real database not exist this, real database only exits data from sql), it can not access real database.
how can I save and select data from real database?
Spring transactional tests rollback automatically by default. See the documentation for how to customize this.
I am using NUnit for test units. I have my interface on the domain so i am ready to make implementation of those interfaces in the persistence layer. My question is how do you actually make the unit tests for testing those repositories ? i believe this isnt a good idea to test directly from the database. Ive heard poeple that are using SQLite but it is okay to use mocks instead ? why are the poeple using SQLite for in-memory database when you can provide a mock with actuals entities ?
Any example would be welcome too.
Note: This is intended to be repositories coded in C# that gonna use NHibernate and Fluent NHibernate as mapping.
Thanks.
It of course depends, but in most cases I'd say it's generally enough to just mock the repositories in your tests and using the in-memory SQLite database only to test your mappings (FluentNHibernate Persistence specification testing).
For the NUnit mappings tests with SQLite I'm using the following base class:
public abstract class MappingsTestBase
{
[SetUp]
public void Setup()
{
_session = SessionFactory.OpenSession();
BuildSchema(_session);
}
[TestFixtureTearDown]
public void Terminate()
{
_session.Close();
_session.Dispose();
_session = null;
_sessionFactory = null;
_configuration = null;
}
#region NHibernate InMemory SQLite Session
internal static ISession _session;
private static ISessionFactory _sessionFactory;
private static Configuration _configuration;
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
FluentConfiguration configuration = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql)
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<NHibernateSession>())
.ExposeConfiguration(c => _configuration = c);
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
private static void BuildSchema(ISession session)
{
SchemaExport export = new SchemaExport(_configuration);
export.Execute(true, true, false, session.Connection, null);
}
#endregion
}
An example mappings test class deriving from the above base class could then look like the following:
[TestFixture]
public class MappingsTest : MappingsTestBase
{
[Test]
public void Persistence_Employee_ShouldMapCorrectly()
{
Category employee = new PersistenceSpecification<Employee>(_session)
.CheckProperty(e => e.Id, 1)
.CheckProperty(e => e.FirstName, "John")
.CheckProperty(e => e.LastName, "Doe")
.VerifyTheMappings();
...
Assert.Equals(employee.FirstName, "John");
...
}
}
Personally I'd do functional testing of the repositories against an actual database (possibly SQL Express). You could run those tests in CI only once a day.
All the unit tests for other classes can safely assume that the repositories work and use mock repositories.
EDIT: The above presumes that your repositories are solely used for data access; they basically just use LINQ or HQL. Keep the business logic out of them!
I am very new to Unit Testing, so I am starting on my first set of tests today. I am using the Library JustMock from Telerik. Though any unit testing information is good. I am having a bit of trouble with an interface service that passes through my method. Below is my MembershipController.Register(model) method...
[CaptchaValidator]
[HttpPost]
public ActionResult Register(Models.Membership.Registration model)
{
// just for good mesure, truncate any spaces that are given
System.Text.RegularExpressions.Regex.Replace(model.Email, #"\s", "");
if (ModelState.IsValid)
{
// Attempt to register the User and return any applicable status that has to do
// with the result.
var createStatus = membershipService.RegisterMember(model.Email, model.Password);
// if the member is able to be created successfully, log them in now and begin the
// authentication portion of the registration, otherwise, display the registration
// errors and return to the view.
if (createStatus == Membership.MemberCreateStatus.Success)
{
formsAuthentication.SignIn(model.Email, false /* createPersistentCookie */);
return RedirectToAction("Success");
}
else
{
ModelState.AddModelError("", Membership.Validator.ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
And here is the paltry test I am trying to run...
[TestMethod]
public void Register_Post_ReturnsRedirectOnSuccess()
{
// Arrange
var controller = Mock.Create<Web.Controllers.MembershipController>();
var repository = Mock.Create<Membership.IMembershipService>();
Mock.Arrange(() => repository.RegisterMember("acceptible#email.com", "acceptiblePassword")).Returns(Membership.MemberCreateStatus.Success);
// Model
var model = new Web.Models.Membership.Registration
{
Email = "acceptible#email.com",
Password = "acceptiblePassword",
ConfirmPassword = "acceptiblePassword"
};
// Act
var result = controller.Register(model);
// Assert
Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
The test fails because membershipService is resolving as null. I'm not sure what to do here. This is my first forray into the Unit Testing aspect of ASP.NET MVC. Can anyone give me some advice?
I am using Ninject to inject IMembershipService through the Constructor. It is implemented by the class MembershipService. The code runs fine when I run it, but the unit tests fail.
I don't see you passing repository anywhere into your controller. Normally you would have IMembershipService as a parameter in your controller's constructor that you can then pass in when needed or use MVC's Service Locator to grab the Ninject instance and pass it in.
:)
The following is some background info on this post. You can just skip to the question if you like:
In this excellent article (http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx) the author contends that "When using NHibernate we generally want to test only three things:
1) that properties are persisted,
2) that cascade works as expected
3) that queries return the correct result.
-) that mapping is complete & correct (implied)
My take is that he goes on to say that SQLite can and should be the unit test tool of choice to do all of the above. It should be noted that the author seems to be one of more experienced and skilled NHib developers out there, and though he doesn't expressly say so in the article, he implies in a question later that the domain can and should be handling some of SQLite's shortcomings.
QUESTION:
How do you use SQLite to test cascade relationships, especially given that it does not check foreign key constraints. How do you test your model to make sure foreign key constraints will not be a db issue.
Here are some units tests I came up with to test cascade behavior. The model is simply a Department that can have zero to many StaffMembers, with cascade set to NONE.
[Test]
public void CascadeSaveIsNone_NewDepartmentWithFetchedStaff_CanSaveDepartment()
{
_newDept.AddStaff(_fetchedStaff);
Assert.That(_newDept.IsTransient(), Is.True);
_reposDept.SaveOrUpdate(_newDept);
_reposDept.DbContext.CommitChanges();
Assert.That(_newDept.IsTransient(), Is.False);
}
[Test]
public void CascadeSaveIsNone_NewDepartmentWithFetchedStaff_CannotSaveNewStaff()
{
_newDept.AddStaff(_newStaff);
Assert.That(_newDept.IsTransient(), Is.True);
Assert.That(_newStaff.IsTransient(), Is.True);
_reposDept.SaveOrUpdate(_newDept);
_reposDept.DbContext.CommitChanges();
Assert.That(_newDept.IsTransient(), Is.False);
Assert.That(_newStaff.IsTransient(), Is.True);
}
[Test]
public void CascadeDeleteIsNone_FetchedDepartmentWithFetchedStaff_Error()
{
_fetchedDept.AddStaff(_fetchedStaff);
_reposDept.SaveOrUpdate(_fetchedDept);
_reposStaff.DbContext.CommitChanges();
_reposDept.Delete(_fetchedDept);
var ex = Assert.Throws<GenericADOException>(() => _reposDept.DbContext.CommitChanges());
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("could not delete:"));
Console.WriteLine(ex.InnerException.Message);
Assert.That(ex.InnerException.Message, Text.Contains("The DELETE statement conflicted with the REFERENCE constraint"));
}
[Test]
public void Nullable_NewDepartmentWithNoStaff_CanSaveDepartment()
{
Assert.That(_newDept.Staff.Count(), Is.EqualTo(0));
var fetched = _reposDept.SaveOrUpdate(_newDept);
Assert.That(fetched.IsTransient(), Is.EqualTo(false));
Assert.That(fetched.Staff.Count(), Is.EqualTo(0));
}
The third test, ".._FetchedDepartmentWithFetchedStaff_Error" works against Sql Server, but not SQLite since the latter does not check foreign key constraints.
Here are tests for the other side of the relationship; a StaffMember can have one Department, with cascade set to NONE.
[Test]
public void CascadeSaveIsNone_NewStaffWithFetchedDepartment_CanSaveStaff()
{
_newStaff.Department = _fetchedDept;
_reposStaff.SaveOrUpdate(_newStaff);
_reposStaff.DbContext.CommitChanges();
Assert.That(_newStaff.Id, Is.GreaterThan(0));
}
[Test]
public void CascadeSaveIsNone_NewStaffWithNewDepartment_Error()
{
_newStaff.Department = _newDept;
Assert.That(_newStaff.IsTransient(), Is.True);
var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("not-null property references a null or transient value"));
}
[Test]
public void CascadeDeleteIsNone_FetchedStaffWithFetchedDepartment_DeletesTheStaff_DoesNotDeleteTheDepartment()
{
_newStaff.Department = _fetchedDept;
_reposStaff.SaveOrUpdate(_newStaff);
_reposStaff.DbContext.CommitChanges();
_reposStaff.Delete(_newStaff);
Assert.That(_reposStaff.Get(_newStaff.Id), Is.Null);
Assert.That(_reposDept.Get(_fetchedDept.Id), Is.EqualTo(_fetchedDept));
}
[Test]
public void NotNullable_NewStaffWithUnknownDepartment_Error()
{
var noDept = new Department("no department");
_newStaff.Department = noDept;
var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("not-null property references a null or transient"));
}
[Test]
public void NotNullable_NewStaffWithNullDepartment_Error()
{
var noDept = new Department("no department");
_newStaff.Department = noDept;
var ex = Assert.Throws<PropertyValueException>(() => _reposStaff.SaveOrUpdate(_newStaff));
Console.WriteLine(ex.Message);
Assert.That(ex.Message, Text.Contains("not-null property references a null or transient"));
}
These tests succed against Sql Server and SQLite. Can I trust the SQLite tests? Are these worthwhile tests?
Cheers,
Berryl
As i understand the article it is about testing the NHibernate Mapping. In my opinion this has nothing to do with db related issues but with testing the nhibernate attributes you set in your mapping. There is no need to assert that it is not possible to create invalid data: you only have to proof that your code creates the desired result and/or checks the things you want to check. You can test cascade, cascade-delete and delete-orphan. whatever you want the way you do it in the tests working with sqlite. But the third test tries to test the constraint, which is nothing nhibernate worries about.
If you want to test your Db contraints you should indeed use your production db and not sqlite. You could do it with or without hibernate but this has nothing to do with your mapping.
If you on the other hand really want a workarround for your Foreign Key tests with SQLite you could try to use this foreign_key_trigger_generator. I haven't tried but it seems to generate before-insert-triggers that assure the existance of the referenced Pk.
Maybe you could write a comment wheather this tool is usefull.