I am creating a set of conventions for Fluent nHibernate. I create a few conventions, like primary key, foreign key and many-to-many table.
I would like to be able to test out these conventions with an in memory database to see if I coded these conventions correctly.
Currently, I am setting up nHibernate using an SQlite in-memory database like this:
Configuration configuration = null;
var factory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<DataLayer>();
m.FluentMappings.Conventions.AddFromAssemblyOf<DataLayer>();
})
.ExposeConfiguration((c) => configuration = c)
.BuildSessionFactory();
var session = factory.OpenSession();
var export = new SchemaExport(configuration);
export.Execute(true, true, false, session.Connection, null);
I create a test case, but I don't know test a naming convention. How can I achieve some unit tests on a conventions using a Visual Studio test project?
What I did is not testing against the database, but just test the generated mapping. So for example I have a convention that says that all foreign keys are written like ID and I test it like (I use xunit and not mstest, but hopefully you can get the concept...):
[Fact]
public void AddDefaultConventions_ShouldNameMappingToForeinKeyCorrectly()
{
var configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008)
.Mappings(m =>
{
m.FluentMappings.Conventions.Add(new CustomForeignKeyConvention());
m.FluentMappings.Add<TestClassMap>();
m.FluentMappings.Add<TestClass2Map>();
})
.BuildConfiguration();
var typeMapping = configuration.GetClassMapping(typeof(TestClass2));
var property = typeMapping.GetProperty("Parent");
Assert.Equal("ParentID", property.ColumnIterator.First().Text);
}
private class TestClass
{
public virtual int ID { get; set; }
}
private class TestClass2
{
public virtual int ID { get; set; }
public virtual TestClass Parent { get; set; }
}
private class TestClassMap : ClassMap<TestClass>
{
public TestClassMap()
{
Id(x => x.ID);
}
}
private class TestClass2Map : ClassMap<TestClass2>
{
public TestClass2Map()
{
Id(x => x.ID);
References(x => x.Parent);
}
}
Btw. it wouldn't be too difficult to test against a DB, just try to select something from TestClass2 and make sure no exception is thrown... but I think the way I showed is easier and I trust that when FluentNhibernate can generate the correct NHibernate mapping, NHibernate can generate the correct queries for me.
Related
I have written an IDbCommandInterceptor to intercept calls to EntityFrameworkCore DbContext. Now I would like to write unit tests for the interceptor. I am able to intercept and run custom logic for an actual db (tested with SqlServer and MySql), however when testing with an InMemoryDatabase (provided by .Net for testing purposes), my interceptor methods are never called.
Here's how I'm setting up my unit test:
TestDbContext class:
public class TestDbContext : DbContext
{
public TestDbContext(DbContextOptions options) : base(options) { }
public DbSet<User> Users { get; set; }
}
public class User
{
public int UserId { get; set; }
}
This is how I'm initializing the context in my test:
private TestDbContext GetTestDbContext()
{
var options = new DbContextOptionsBuilder<TestDbContext>()
.UseInMemoryDatabase(databaseName: "TestDB")
.AddInterceptors(new MyInterceptor())
.Options;
var context = new TestDbContext(options);
// Seed data
context.Users.Add(new User { UserId = 1 });
context.Users.Add(new User { UserId = 2 });
context.Users.Add(new User { UserId = 3 });
context.SaveChanges();
return context;
}
Now, when I call SaveChanges() or do a query on this context, I expect it to invoke my MyInterceptor's overridden ReaderExecuted() method the same way it does when I use an actual db.
Am I missing something? Or DbCommandInterceptor does not work with InMemoryDatabase?
The InMemory provider is not a relational provider, and therefore does not implement DbCommand interceptors. You can use a SQL Server / SQL Server LocalDb / SQlite database for this kind of testing.
I'm using NancyFX with FluentValidation, as documented at https://github.com/NancyFx/Nancy/wiki/Nancy-and-Validation. My web app is running fine and validation is working perfectly, but when I try to unit test any of the modules that use validation, I'm getting an error
Nancy.Validation.ModelValidationException : No model validator factory could be located.
Please ensure that you have an appropriate validation package installed, such as
one of the Nancy.Validation packages.
I've verified that my unit test project has references to the Nancy.Validation.FluentValidation and FluentValidation assemblies.
My test code looks like this:
public class ArticleModuleTests {
private Browser browser;
private IDatabase db;
const int USER_ID = 123;
const int ARTICLE_ID = 456;
[SetUp]
public void SetUp() {
var user = new User { Username = "test", Id = USER_ID };
db = A.Fake<IDatabase>();
browser = new Browser(with => {
with.Module<ArticleModule>();
with.RequestStartup((container, pipelines, context) => context.CurrentUser = user);
with.Dependency(db);
});
}
[Test]
public void User_Can_Publish_Article() {
var article = new { title = "Test" };
var result = browser.Post($"/users/{USER_ID}/articles", with => {
with.HttpRequest();
with.Body(JsonConvert.SerializeObject(article));
});
result.StatusCode.ShouldBe(HttpStatusCode.BadRequest);
}
}
My module code is:
public class ArticlesModule : NancyModule {
private IDatabase database;
public ArticlesModule(IDatabase db) {
this.database = db;
Post["/users/{id:int}/articles"] = args => PostArticle(args.id);
}
private dynamic PostArticle(int userId) {
var article = this.Bind<Article>();
var validation = this.Validate(article);
if (!validation.IsValid) return Negotiate.WithModel(validation).WithStatusCode(HttpStatusCode.BadRequest);
database.CreateArticle(userId, article);
return NegotiatorExtensions.WithModel(Negotiate, result)
.WithStatusCode(HttpStatusCode.Created)
.WithHeader("Location", $"http://whatever/users/{userId}/articles/{article.Id}");
}
}
and my validation class is:
public class ArticleValidator : AbstractValidator<Article> {
public ArticleValidator() {
RuleFor(article => article.Title)
.NotEmpty()
.WithMessage("The \"title\" property is required");
RuleFor(article => article.Title)
.Length(2, 50)
.WithMessage("The \"title\" property must be between 2 and 50 characters");
}
}
The NancyFX docs say "Create a validation class... There is no need to register it anywhere as it is automatically detected." - but I'm guessing whatever automatic detection is wired up isn't firing for a unit test project. I'm building on .NET 4.5.2 and using NCrunch as my test runner; what do I need to do to get my test code to pick up the same validation classes as my application modules?
OK, turns out that because NancyFX detects and instantiates validation classes automatically, there's no explicit references in my code to Nancy.Validation.FluentValidation, and so NCrunch is omitting this assembly when building my test project. Setting "Copy referenced assemblies to workspace" in the NCrunch project settings fixed it.
I'm quite new to unit testing and I need an hand to understand if I'm doing things in the correct way. My major problem is regarding the DB testing... Here's my code then I'll expose my perplexities
Consider this class that's an item of a pipeline I've to perform
public class RetrieveApplicationUsernamePipelineStep : IPipelineStep
{
public const string RetrieveApplicationUsernameKey = "RetrieveApplicationUsername";
private readonly IRetrieveApplicationUserRepository repository;
public int Order => 3;
public string Name => RetrieveApplicationUsernameKey;
public RetrieveApplicationUsernamePipelineStep(IRetrieveApplicationUserRepository repository)
{
this.repository = repository;
}
public async Task<IDictionary<string, object>> Action(IDictionary<string, object> context)
{
string res = await repository.GetApplicationUser(context);
context[Resources.ApplicationUser] = res;
return context;
}
}
I wrote the following tests
[TestFixture]
public class RetrieveApplicationUsernamePipelineStepTests
{
private IRetrieveApplicationUserRepository retrieveApplicationUserRepository;
[OneTimeSetUp]
public void Start()
{
var configuration = new ConfigurationFromConfigFile();
retrieveApplicationUserRepository = new RetrieveApplicationUserRepository(configuration);
}
[Test]
public async Task ActionSuccessfullyCompleted()
{
var context = new Dictionary<string, object>();
var repository = Substitute.For<IRetrieveApplicationUserRepository>();
repository.GetApplicationUser(context).Returns("user1");
var pipeline = new RetrieveApplicationUsernamePipelineStep(repository);
var res = await pipeline.Action(context);
Assert.IsNotNull(res[Resources.ApplicationUser]);
Assert.IsNotEmpty((string)res[Resources.ApplicationUser]);
}
[Test]
public void ActionFailingCompleted()
{
var context = new Dictionary<string, object>();
var repository = Substitute.For<IRetrieveApplicationUserRepository>();
repository.GetApplicationUser(context).Throws(new UserMappingNotFoundException());
var pipeline = new RetrieveApplicationUsernamePipelineStep(repository);
Assert.ThrowsAsync<UserMappingNotFoundException>(async () => await pipeline.Action(context));
}
[Test]
public void NameTest()
{
var pipeline = new RetrieveApplicationUsernamePipelineStep(retrieveApplicationUserRepository);
Assert.IsTrue(pipeline.Name == RetrieveApplicationUsernamePipelineStep.RetrieveApplicationUsernameKey);
}
[Test]
public void OrderTest()
{
var pipeline = new RetrieveApplicationUsernamePipelineStep(retrieveApplicationUserRepository);
Assert.IsTrue(pipeline.Order == 3);
}
}
And those test works fine since for ActionSuccessfullyCompleted and ActionFailingCompleted I substitute the IRetrieveApplicationUserRepository's result with my expected one.
The real implementation of ther repository is
public class RetrieveApplicationUserRepository : IRetrieveApplicationUserRepository
{
#region Variables
private readonly IConfiguration configuration;
#endregion
#region Ctor
public RetrieveApplicationUserRepository(IConfiguration configuration)
{
this.configuration = configuration;
}
#endregion
#region IRetrieveApplicationUserRepository
public async Task<string> GetApplicationUser(IDictionary<string, object> context)
{
if (configuration.AppSettings[Resources.ApplicationUserFromDomainUserKey] == null)
throw new KeyNotFoundException(Resources.ApplicationUserFromDomainUserKey);
if (string.IsNullOrEmpty(configuration.ConnectionString))
throw new NullReferenceException();
string storedProcedure = configuration.AppSettings.Get(Resources.ApplicationUserFromDomainUserKey);
string result;
using (var sqlConnection = new SqlConnection(configuration.ConnectionString))
{
using (var sqlCommand = new SqlCommand(storedProcedure, sqlConnection))
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.AddWithValue("#DOMAINUSER", context[Resources.DomainUser]);
sqlCommand.Parameters.AddWithValue("#DOMAIN", context[Resources.DomainName]);
sqlCommand.Parameters.AddWithValue("#APPID", context[Resources.ApplicationId]);
sqlConnection.Open();
result = (string)await sqlCommand.ExecuteScalarAsync();
}
}
if (result == null)
throw new UserMappingNotFoundException();
return result;
}
#endregion
}
Here're the questions :
Are the test I wrote correct?
I've seen using Resharper's Code Coverage that it wants me to test-cover the properties...is there a way I can avoid this? is this test meaningful?
What's your approach when you've to unit test component that're related to DB? Have you got a real-db that's used for test? Consider that the real DB is about 10Gb so I don't want to have a copy as mdf (condider I can have this) just to test a small portion of the DB
Talking with my colleagues they told me to use test just for TDD while I wish to use them to avoid regressions
Going back to the DB question, I don't want to have a test where I write if username is "John" and maybe tomorrow John user won't be present in the DB anymore,so that the test I expect to pass wont' pass anoymore
The usual approach is to isolate the database side with an abstraction, so you can provide a test dummy (mock, fake, etc) of that abstraction. Only test an actual database when you do the integration testing.
For the tests of database stored procedures, you may well want a different test harness, creating a new test database in memory (equivalently, in a RAM-backed filesystem). You only need to populate enough data for the individual test (we're doing functional testing here, not performance testing), and you may be able to retain table structure across tests with judicious use of rollback.
I have done this, but it's some time ago, so I'll refrain from giving examples that may be no longer state-of-the-art (even if the code does still exist, and if I could find it).
So I'm working with Umbraco 6.12 and having great difficulty been able to test a RenderMvcController.
I have implemented IApplicationEventHandler in my Global.ascx and Ninject is working fine and as expected when running the application - all good.
However, unit testing these controllers is a different matter. I found this, and have added the latest reply:
http://issues.umbraco.org/issue/U4-1717
I now have this lovely hack in my SetUp:
Umbraco.Web.UmbracoContext.EnsureContext(new HttpContextWrapper(new HttpContext(new HttpRequest("", "http://www.myserver.com", ""), new HttpResponse(null))), ApplicationContext.Current);
Which has got around the original UmbracoContext cannot be null, but is now throwing:
Current has not been initialized on Umbraco.Web.PublishedCache.PublishedCachesResolver. You must initialize Current before trying to read it.
The published caches resolver also seems to be hidden behind internal and protected stuff, which I can't use reflection to hack at as I can't init anything to pass into SetProperty reflection.
It's really frustrating, I'm loving v6, and using uMapper is very nice. I can inject a repo, service, command or query at will into the controllers and life is good - I just can't cover the controllers!
Any help on this would be greatly appreciated.
Thanks.
To unit test a Umbraco RenderMvcController, you need to grab the source code from github, compile the solution yourself, and get the Umbraco.Tests.dll and reference it on your test project.
In addition to that, you need to reference the SQLCE4Umbraco.dll which is distributed with the Umbraco packages, and Rhino.Mocks.dll which is internally for mocking.
To help you with this, I have compiled put the Umbraco.Tests.dll for Umbraco 6.1.5 and put it together with the Rhino.Mocks.dll and put it on this zip file.
Finally, derive your test from BaseRoutingTest, override the DatabaseTestBehavior to
NoDatabasePerFixture, and get the UmbracoContext and HttpBaseContext by calling the GetRoutingContext method, as in the code below:
using System;
using Moq;
using NUnit.Framework;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace UnitTests.Controllers
{
public class Entry
{
public int Id { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string[] Tags { get; set; }
public DateTime Date { get; set; }
}
public interface IBlogService
{
Entry GetBlogEntry(int id);
}
public class BlogEntryController : RenderMvcController
{
private readonly IBlogService _blogService;
public BlogEntryController(IBlogService blogService, UmbracoContext ctx)
: base(ctx)
{
_blogService = blogService;
}
public BlogEntryController(IBlogService blogService)
: this(blogService, UmbracoContext.Current)
{
}
public override ActionResult Index(RenderModel model)
{
var entry = _blogService.GetBlogEntry(model.Content.Id);
// Test will fail if we return CurrentTemplate(model) as is expecting
// the action from ControllerContext.RouteData.Values["action"]
return View("BlogEntry", entry);
}
}
[TestFixture]
public class RenderMvcControllerTests : BaseRoutingTest
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
[Test]
public void CanGetIndex()
{
const int id = 1234;
var content = new Mock<IPublishedContent>();
content.Setup(c => c.Id).Returns(id);
var model = new RenderModel(content.Object, CultureInfo.InvariantCulture);
var blogService = new Mock<IBlogService>();
var entry = new Entry { Id = id };
blogService.Setup(s => s.GetBlogEntry(id)).Returns(entry);
var controller = GetBlogEntryController(blogService.Object);
var result = (ViewResult)controller.Index(model);
blogService.Verify(s => s.GetBlogEntry(id), Times.Once());
Assert.IsNotNull(result);
Assert.IsAssignableFrom<Entry>(result.Model);
}
private BlogEntryController GetBlogEntryController(IBlogService blogService)
{
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
var controller = new BlogEntryController(blogService, umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}
}
}
This code has only been tested in Umbraco 6.1.5.
According to the core team, you should include the Umbraco.Tests library and inherit your test from BaseUmbracoApplicationTest. That will setup a valid UmbracoApplication and UmbracoContext.
https://groups.google.com/forum/?fromgroups=#!topic/umbraco-dev/vEjdzjqmtsU
I have raised this on the Umbraco forums and there are several replies which may help you.
See here:
http://our.umbraco.org/forum/developers/api-questions/37255-How-can-I-unit-test-a-class-inheriting-from-SurfaceController
Essentially, you can .. just ... but requires some reflection because some of the key classes and interfaces are internal. As Luke's last post points out, this is because the functionality is currently a bit of a moving target.
I am trying to setup database unit tests using the Sqllite.InMemory functionality.
If I run an unit test, everything is working fine. If I run the same test a second time I get an System.Data.SQLite.SQLiteException: no such table: Person
After waiting for a while and/or (?) restarting Visual studio I can run the unit test once again.
Is there something wrong with the configuration or the session handling?
public abstract class InMemoryDatabaseFixture : IDisposable
{
private const string ConnectionString
= "Data Source=:memory:;Version=3;New=True;Pooling=True;Max Pool Size=1;";
private readonly ISessionFactory _sessionFactory;
private readonly ISession _session;
protected InMemoryDatabaseFixture()
{
var config = SQLiteConfiguration.Standard.InMemory().ShowSql().ConnectionString(ConnectionString);
_sessionFactory = Fluently.Configure()
.Database(config)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionContainer>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
SessionContainer = MockRepository.GenerateMock<ISessionContainer>();
SessionContainer.Stub(sc => sc.SessionFactory).Return(_sessionFactory);
SessionContainer.Stub(sc => sc.Session).Return(_session);
}
protected ISessionContainer SessionContainer { get; private set; }
public void Dispose()
{
_sessionFactory.Dispose();
_session.Dispose();
}
}
Here a simple usage of the base class:
[TestFixture]
public class FinderFixture : InMemoryDatabaseFixture
{
[Test]
public void Test()
{
var finder = new Finder(SessionContainer);
var result = finder.Find();
Assert.That(result, Is.Not.Null);
}
}
Update: After some trials here is finally my working configuration. Exporting the schema after building the SessionFactory is doing the magic.
Configuration configuration = null;
_sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.ExposeConfiguration(cfg => configuration = cfg)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionContainer>())
.BuildSessionFactory();
_session = _sessionFactory.OpenSession();
var export = new SchemaExport(configuration);
export.Execute(true, true, false, _session.Connection, null);
You have requested that connection pooling in the ADO.NET provider be enabled. This will keep the underlying connection active even after NHibernate has closed it.
In my own unit tests, I have simply (based on your original code):
_sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionContainer>())
.ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true))
.BuildSessionFactory();
Also, since the session is generated from the session factory, it would be prudent to dispose all sessions before disposing the session factory.