With JustMock I can mock DataContext tables with lists in Linq to SQL easily like this, where an IEnumerable is taking the place of each DataContext's table through ReturnsCollection() allowing me to plug in fake data:
[TestMethod]
public void ShouldGetManagersByHireDate()
{
var context = Mock.Create<MyDataContext>();
Mock.Arrange(()=> context.Employees).ReturnsCollection(GetFakeEmployees());
Mock.Arrange(() => context.Managers).ReturnsCollection(GetFakeManagers());
var repository = new EmployeeRepository(context);
var managers = repository.GetManagersByHireDate(new DateTime(2002, 1, 1), DateTime.Now);
Assert.AreEqual(1, managers.Count());
Assert.AreEqual(1, managers.FirstOrDefault().ID);
}
private IEnumerable<Employee> GetFakeEmployees()
{
return new List<Employee> {
new Employee { ID = 1, HireDate = new DateTime(2004, 12, 1) },
new Employee { ID = 2, HireDate = new DateTime(2006, 7, 1) },
new Employee { ID = 3, HireDate = new DateTime(2009, 3, 1) }
};
}
private IEnumerable<Manager> GetFakeManagers()
{
return new List<Manager> {
new Manager { ID = 1 }
};
}
And this would be the method under test:
public IQueryable<Employee> GetManagersByHireDate(DateTime start, DateTime end)
{
return from e in context.Employees
join m in context.Managers on e.ID equals m.ID
where e.HireDate >= start && e.HireDate <= end
select e;
}
I am looking for some way of performing the same magic allowing me to use IEnumerable<T> in place of Table<T> for the purpose of testing Linq to SQL, preferably in FakeItEasy.
Linq to SQL isn't the easiest thing to test using open source tools. Hopefully, this approach may work for you.
FakeItEasy doesn't have a method exactly like JustMock's ReturnCollection that would allow you to mock out an ITable to return an IEnumerable. One option you have is to create a MockableTable similar to the one shown here. Then to populate your Table, you could have something like
private ITable<Employee> GetFakeEmployees() {
List<Employee> sampleData = /* fill it up with employees */
var employeeTable = new MockableTable<Employee>(null, sampleData.AsQuerable());
return employeeTable;
}
Also, FakeItEasy won't intercept the Employees property on the DataContext since it's a non virtual property on a concrete class. You could create a simple custom base class and have MyDataContext class derive directly from that.
public abstract class CustomDataContext : DataContext, ICustomDataContext {
}
public interface ICustomDataContext {
ITable<Employee> { get; }
}
The main point here is to leverage the interface for your mock. Then in your test method, you would have:
[TestMethod]
public void ShouldGetManagersByHireDate() {
var context = A.Fake<ICustomDataContext>();
A.CallTo(()=> context.Employees).Returns(GetFakeEmployees());
var repository = new EmployeeRepository(context);
var managers = repository.GetManagersByHireDate(new DateTime(2002, 1, 1), DateTime.Now);
Assert.AreEqual(1, managers.Count());
Assert.AreEqual(1, managers.FirstOrDefault().ID);
}
I haven't actually compiled this, but concept should be stable. Relying on the abstractions will make your code more testable and easier to mock.
Hope this helps somewhat.
The way I have done this using FakeItEasy is to add an interface to DataContext and use that as my dependency in my repos. E.g.
public interface IMyDataContext : IDisposable
{
IQueryable<Employee> Employees { get; }
// etc.
}
public partial class MyDataContext: IMyDataContext
{
IQueryable<Message> IMyDataContext.Employees
{
get { return this.Employees; }
}
// etc.
}
public class EmployeeRepository
{
public EmployeeRepository(IMyDataContext context)
{
// etc.
}
}
And in my test:
var context = A.Fake<IMyDataContext>();
A.CallTo(() => context.Employees).Returns(new[] { new Employee { Name = "John", Name = "Fred" }.AsQueryable());
var repository = new EmployeeRepository(context)
I don't think any considerations surrounding ITable are required.
Related
I am trying to unit test my service layer as advised by #NKosi Here. I am able to do the integration test successfully by implementing the actual factory implementation without mocking anything but can't do the unit test (by mocking IDbConnection and my SQL connection factory class) as Dapper query executing fails with the error 'Object not set to an instance of an object'.
My IDbConnection factory and its implementation is as follow
public interface IDbConnectionFactory
{
IDbConnection CreateConnection();
}
public class ConnectionSetings
{
public string Name { get; set; }
}
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly ConnectionSetings connectionSettings;
public SqlConnectionFactory(ConnectionSetings connectionSettings)
{
this.connectionSettings = connectionSettings;
}
public IDbConnection CreateConnection()
{
return new SqlConnection(connectionSettings.Name);
}
}
And the XUnit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var connection = new Mock<IDbConnection>();
var dbConnectionFactory = new Mock<IDbConnectionFactory>();
dbConnectionFactory.Setup(x => x.CreateConnection()).Returns(connection.Object);
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactory.Object).Get(1);
//Assert
result.Name.Equals("Test Name 1");
//Assert.Equal("Test Name 1", result.Name);
}
And the Service Layer is as follow
public class SqlRestaurantDataCL : IRestaurantDataCL
{
private readonly IDbConnectionFactory factory;
public SqlRestaurantDataCL(IDbConnectionFactory factory)
{
this.factory = factory;
}
public Restaurant Get(int id)
{
using (var connection = factory.CreateConnection())
{
var selectSql = #"SELECT * From Restaurants Where Id = #Id";
var restaurant = connection.QuerySingleOrDefault<Restaurant>(selectSql, new
{
id
});
return restaurant;
}
}
}
Following is the error screenshot
Following is the answer to my question if anyone is in similar situation. Before following this solution, I would suggest to read #NKosi comments above and consult #Mikhail's solution Here.
ServiceStack.OrmLite.Sqlite package added to use in memory appraoch
internal class InMemoryDatabase
{
private readonly OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
public void Insert<T>(IEnumerable<T> items)
{
using (var db = this.OpenConnection())
{
db.CreateTableIfNotExists<T>();
foreach (var item in items)
{
db.Insert(item);
}
}
}
}
Data Access layer is as follow
public IEnumerable<Restaurant> GetAll()
{
using (var connection = factory.CreateConnection())
{
//return connection.Query<Restaurant>("Select * From [dbo].[Restaurants] Order By Name");
return connection.Query<Restaurant>("Select * From Restaurant Order By Name");
}
}
Unit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var restaurants = new List<Restaurant>
{
new Restaurant { Id = 1, Name = "Test Name 1", Cuisine = CuisineType.None},
new Restaurant { Id = 2, Name = "Test Name 2", Cuisine = CuisineType.French},
new Restaurant { Id = 3, Name = "Test Name 3", Cuisine = CuisineType.German},
new Restaurant { Id = 4, Name = "Test Name 4", Cuisine = CuisineType.Italian},
new Restaurant { Id = 5, Name = "Test Name 5", Cuisine = CuisineType.None}
};
var db = new InMemoryDatabase();
db.Insert(restaurants);
var connection = new Mock<IDbConnection>();
var dbConnectionFactoryMock = new Mock<IDbConnectionFactory>();
dbConnectionFactoryMock.Setup(c => c.CreateConnection()).Returns(db.OpenConnection());
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactoryMock.Object).GetAll();
//Assert
result.Should().BeEquivalentTo(restaurants);
}
I am planning to write an ActionFilter for business validation and in which some services will be resolved via Service Locator(I know this is not good practice and as far as possible i avoid Service Locator pattern, but for this case i want to use it).
OnActionExecuting method of the filter is something like this:
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
// get validator for input;
var validator = actionContext.HttpContext.RequestServices.GetService<IValidator<TypeOfInput>>();// i will ask another question for this line
if(!validator.IsValid(input))
{
//send errors
}
}
Is it possible to write unit test for above ActionFilterand how?
Here is an sample on how to create a mock (using XUnit and Moq framework) to verify that the IsValid method is called and where the mock returns an false.
using Dealz.Common.Web.Tests.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using System;
using Xunit;
namespace Dealz.Common.Web.Tests.ActionFilters
{
public class TestActionFilter
{
[Fact]
public void ActionFilterTest()
{
/****************
* Setup
****************/
// Create the userValidatorMock
var userValidatorMock = new Mock<IValidator<User>>();
userValidatorMock.Setup(validator => validator
// For any parameter passed to IsValid
.IsValid(It.IsAny<User>())
)
// return false when IsValid is called
.Returns(false)
// Make sure that `IsValid` is being called at least once or throw error
.Verifiable();
// If provider.GetService(typeof(IValidator<User>)) gets called,
// IValidator<User> mock will be returned
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock.Setup(provider => provider.GetService(typeof(IValidator<User>)))
.Returns(userValidatorMock.Object);
// Mock the HttpContext to return a mockable
var httpContextMock = new Mock<HttpContext>();
httpContextMock.SetupGet(context => context.RequestServices)
.Returns(serviceProviderMock.Object);
var actionExecutingContext = HttpContextUtils.MockedActionExecutingContext(httpContextMock.Object, null);
/****************
* Act
****************/
var userValidator = new ValidationActionFilter<User>();
userValidator.OnActionExecuting(actionExecutingContext);
/****************
* Verify
****************/
// Make sure that IsValid is being called at least once, otherwise this throws an exception. This is a behavior test
userValidatorMock.Verify();
// TODO: Also Mock HttpContext.Response and return in it's Body proeprty a memory stream where
// your ActionFilter writes to and validate the input is what you desire.
}
}
class User
{
public string Username { get; set; }
}
class ValidationActionFilter<T> : IActionFilter where T : class, new()
{
public void OnActionExecuted(ActionExecutedContext context)
{
throw new NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext actionContext)
{
var type = typeof(IValidator<>).MakeGenericType(typeof(T));
var validator = (IValidator<T>)actionContext.HttpContext
.RequestServices.GetService<IValidator<T>>();
// Get your input somehow
T input = new T();
if (!validator.IsValid(input))
{
//send errors
actionContext.HttpContext.Response.WriteAsync("Error");
}
}
}
internal interface IValidator<T>
{
bool IsValid(T input);
}
}
HttpContextUtils.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
namespace Dealz.Common.Web.Tests.Utils
{
public class HttpContextUtils
{
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
IList<IFilterMetadata> filters,
IDictionary<string, object> actionArguments,
object controller
)
{
var actionContext = new ActionContext() { HttpContext = context };
return new ActionExecutingContext(actionContext, filters, actionArguments, controller);
}
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
object controller
)
{
return MockedActionExecutingContext(context, new List<IFilterMetadata>(), new Dictionary<string, object>(), controller);
}
}
}
As you can see, it's quite a mess, you need to create plenty of mocks to simulate different responses of the actuall classes, only to be able to test the ActionAttribute in isolation.
I like #Tseng's above answer but thought of giving one more answer as his answer covers more scenarios (like generics) and could be overwhelming for some users.
Here I have an action filter attribute which just checks the ModelState and short circuits(returns the response without the action being invoked) the request by setting the Result property on the context. Within the filter, I try to use the ServiceLocator pattern to get a logger to log some data(some might not like this but this is an example)
Filter
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ValidationFilterAttribute>>();
logger.LogWarning("some message here");
context.Result = new JsonResult(new InvalidData() { Message = "some messgae here" })
{
StatusCode = 400
};
}
}
}
public class InvalidData
{
public string Message { get; set; }
}
Unit Test
[Fact]
public void ValidationFilterAttributeTest_ModelStateErrors_ResultInBadRequestResult()
{
// Arrange
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(ILogger<ValidationFilterAttribute>)))
.Returns(Mock.Of<ILogger<ValidationFilterAttribute>>());
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProviderMock.Object;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var actionExecutingContext = new ActionExecutingContext(
actionContext,
filters: new List<IFilterMetadata>(), // for majority of scenarios you need not worry about populating this parameter
actionArguments: new Dictionary<string, object>(), // if the filter uses this data, add some data to this dictionary
controller: null); // since the filter being tested here does not use the data from this parameter, just provide null
var validationFilter = new ValidationFilterAttribute();
// Act
// Add an erorr into model state on purpose to make it invalid
actionContext.ModelState.AddModelError("Age", "Age cannot be below 18 years.");
validationFilter.OnActionExecuting(actionExecutingContext);
// Assert
var jsonResult = Assert.IsType<JsonResult>(actionExecutingContext.Result);
Assert.Equal(400, jsonResult.StatusCode);
var invalidData = Assert.IsType<InvalidData>(jsonResult.Value);
Assert.Equal("some messgae here", invalidData.Message);
}
I am trying to test the AddCategory of the following CategoryService.
My problem is that I am having a hard time understanding what to mock/fake.
My attempt at the test is at the bottom.
I am using MOQ, xUnit and FluentAssertions.
I am using FluentValidation for the validators.
Category Service
public class CategoryService : ValidatingServiceBase, ICategoryService
{
private readonly IUnitOfWork unitOfWork;
private readonly IRepository<Category> categoryRepository;
private readonly IRepository<SubCategory> subCategoryRepository;
private readonly IValidationService validationService;
public CategoryService(
IUnitOfWork unitOfWork,
IRepository<Category> categoryRepository,
IRepository<SubCategory> subCategoryRepository,
IValidationService validationService)
: base(validationService)
{
this.unitOfWork = unitOfWork;
this.categoryRepository = categoryRepository;
this.subCategoryRepository = subCategoryRepository;
this.validationService = validationService;
}
public bool AddCategory(Category category)
{
var validationResult = validationService.Validate(category);
if (!validationResult.IsValid)
{
return false;
}
else
{
categoryRepository.Add(category);
return true;
}
}
public bool DoesCategoryExist(string categoryName)
{
return categoryRepository.Query().SingleOrDefault(x => x.Name == categoryName) != null;
}
}
Validation Service
public class ValidationService : ServiceBase, IValidationService
{
private readonly IValidatorFactory validatorFactory;
public ValidationService(IValidatorFactory validatorFactory)
{
Enforce.ArgumentNotNull(validatorFactory, "validatorFactory");
this.validatorFactory = validatorFactory;
}
public ValidationResult Validate<TEntity>(TEntity entity) where TEntity : class
{
var validator = validatorFactory.GetValidator<TEntity>();
return validator.Validate(entity);
}
}
Validator Factory
public class ValidatorFactory : IValidatorFactory
{
public IValidator GetValidator(Type type)
{
Enforce.ArgumentNotNull(type, "type");
return DependencyResolver.Current.GetService(typeof(IValidator<>).MakeGenericType(type)) as IValidator;
}
public IValidator<T> GetValidator<T>()
{
return DependencyResolver.Current.GetService<IValidator<T>>();
}
}
Category Validator
public class CategoryValidator : AbstractValidator<Category>
{
public CategoryValidator(ICategoryService service)
{
RuleFor(x => x.Name)
.NotEmpty()
.Must((category, name) =>
{
return service.DoesCategoryExist(name);
});
}
}
Unit Test Attempt
[Fact]
public void AddCategory_Should_ReturnTrue()
{
var category = new Category() { Name = "Cat1" };
var unitOfWork = new Mock<IUnitOfWork>();
var categoryRepo = new Mock<IRepository<Category>>();
var subCategoryRepo = new Mock<IRepository<SubCategory>>();
var mockCategoryService = new Mock<ICategoryService>();
var categoryValidator = new CategoryValidator(mockCategoryService.Object);
var validatorFactory = new Mock<IValidatorFactory>();
validatorFactory.Setup(x => x.GetValidator<CategoryValidator>()).Returns(categoryValidator as IValidator<CategoryValidator>);
var validationService = new ValidationService(validatorFactory.Object);
var categoryService = new CategoryService(
unitOfWork.Object,
categoryRepo.Object,
subCategoryRepo.Object,
validationService);
categoryService.AddCategory(category);
}
Well for the AddCategory method, I think you really only need two mocks, one for the ValidationService, and one for the CategoryRepository, because the other dependencies aren't exercised in that function and therefore are irrelevant
(the story might be different of course if your ctor throws on null arguments but in this case I think you are OK - albeit you might consider adding these checks in the future :)
Anyway, being pedantic, I'd nearly be inclined to write two (or more - maybe one for null input to verify it throws or returns false or whatever) "unit" tests for this function;
One to verify that given an invalid category, the function returns false,
One to verify that given a valid category, the function calls Add on the CategoryRepository dependency.
So it would look like this (sorry, this is using MSTest syntax as I'm not familiar with xUnit but it's the same idea). Also have not tested below for typos, etc :)
public void AddCategory_InvalidCategory_ShouldReturnFalse()
{
//Arrange
var mockValidator = new Mock<IValidator>();
//no matter what we pass to the validator, it will return false
mockValidator.Setup(v=>v.Validate(It.IsAny<Category>()).Returns(false);
var sut= new CategoryService(null,null,null,mockValidator.Object);
bool expected = false;
//ACT
bool actual = sut.AddCategory(new Category());
//ASSERT
Assert.AreEqual(expected,actual,"Validator didn't return false as expected");
}
public void AddCategory_ValidCategory_ShouldCallRepositoryAdd()
{
//Arrange
var mockValidator = new Mock<IValidator>();
//no matter what we pass to the validator, it will return true
mockValidator.Setup(v=>v.Validate(It.IsAny<Category>()).Returns(true);
var mockRepo = new Mock<IRepository<SubCategory>>();
mockRepo.Setup(r=>r.Add(It.IsAny<Category>())); //do not know or care what happens as this is a void method.
var sut= new CategoryService(null,mockRepo.Object,null,mockValidator.Object);
bool expected = false;
//ACT
bool actual = sut.AddCategory(new Category());
//ASSERT
mockRepo.Verify(r=>r.Add(It.IsAny<Category>(),Times.Exactly(1),"Repo ADD method not called or called too many times, etc");
Assert.AreEqual(expected,actual,"Add was called BUT the AddCategoryMethod didn't return true as expected"); //and of course you could be totally pedantic and create a new test method for that last assert ;)
}
The reason I favour this approach is because it forces you to consider the behaviour of the method under test, as well as ensuring that you don't involve any dependencies that are not being tested plus it means your test methods only create exactly what they need to in order to run the tests (and of course you can create some setup/teardown helpers to pre-create those mocks for you);
Of course you could put all the above into a single method but for the sake of saving a few LOC I hope you'll agree that having two separate tests to verify two separate behaviours is a more robust approach.
Just my 2c. hope it helps!
I am writing a set of integration tests (Unit tests with MS Test which test that Entity Framework 4.2 is persisting all classes correctly to the database).
When I run all tests one by one they all work fine. When I run them in a group - some of them fail as the wrong number of objects are returned - it would seem that the db is being cleaned down once at the start of the tests and not in between each test - even though I can see a new context being created and then disposed of for each test
Any Ideas?
public class EmptyDataInitializer : DropCreateDatabaseAlways<myContext>
{
protected override void Seed(myContext db)
{
//Do Nothing Create Empty Database
db.SaveChanges();
base.Seed(db);
}
}
A Cut down version of the unit/integration Tests
[TestClass]
public class PersistanceTests
{
//Creating two instances of our Repository so that we can make sure that we are reading from our database rather than in-memory
private myContext _db;
private myContext _dbResults;
private readonly ISettings _configSettings;
public PersistanceTests()
{
_configSettings = MockRepository.GenerateStub<ISettings>();
_configSettings.ConnectionString = "data source=.;initial catalog=myContext_Test; Integrated Security=SSPI; Pooling=false";
Database.SetInitializer(new EmptyDataInitializer());
}
//This is called a single time after the last test has finished executing
[TestCleanup]
public void TearDownTest()
{
_db.Dispose();
_db = null;
_dbResults.Dispose();
_dbResults = null;
}
//This is called each time prior to a test being run
[TestInitialize]
public void SetupTest()
{
_db = new myContext(_configSettings);
_dbResults = new myContext(_configSettings);
// This forces the database to initialise at this point with the initialization data / Empty DB
var count = _db.Accounts.Count();
var resultCount = _dbResults.Accounts.Count();
if (count != resultCount) throw new InvalidOperationException("We do not have a consistant DB experiance.");
}
[TestMethod]
public void OrganisationPersistanceTest()
{
// Arrange
var apple = new Organisation { Name = "Apple" };
_db.Organisations.Add(apple);
// Act
_db.SaveChanges();
var organisationsCount = _dbResults.Organisations.Count();
var organisationsAppleCount = _dbResults.Organisations.Where(a => a.Id == apple.Id).Count();
var result = _dbResults.Organisations.FirstOrDefault(a => a.Id == apple.Id);
// Assert
Assert.IsTrue(organisationsCount == 1, string.Format("Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsCount, 1));
Assert.IsTrue(organisationsAppleCount == 1, string.Format("Apple Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsAppleCount, 1));
Assert.IsNotNull(result, "Organisations Result should not be null");
Assert.AreEqual(result.Name, apple.Name, "Name Mismatch");
}
//A Unit test
[TestMethod]
public void OrganisationWithNumberOfPeople_PersistanceTest()
{
// Arrange
var person = new Person { Firstname = "Bea" };
var anotherPerson = new Person { Firstname = "Tapiwa" };
var apple = new Organisation { Name = "Apple" };
apple.AddPerson(person);
apple.AddPerson(anotherPerson);
_db.Organisations.Add(apple);
// Act
_db.SaveChanges();
var organisationsCount = _dbResults.Organisations.Count();
var organisationsAppleCount = _dbResults.Organisations.Where(a => a.Id == apple.Id).Count();
var result = _dbResults.Organisations.FirstOrDefault(a => a.Id == apple.Id);
var peopleCountInOrganisation = result.People.Count();
// Assert
Assert.IsTrue(organisationsCount == 1, string.Format("Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsCount, 1));
Assert.IsTrue(organisationsAppleCount == 1, string.Format("Apple Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsAppleCount, 1));
Assert.IsNotNull(result, "Organisations Result should not be null");
Assert.AreEqual(result.People.Count, peopleCountInOrganisation, "People count mismatch in organisation Apple - Actual={0}, Expected={1}", peopleCountInOrganisation, 2);
Assert.AreEqual(result.Name, apple.Name, "Name Mismatch");
}
}
Stepping through the tests I can see the SetupTest and TearDownTest methods being called but I it does not seem to clean down the database between tests.
Okay even Better answer - add a database.Initialize(force: true);
into the TestInitialize method.
[TestInitialize]
public void SetupTest()
{
_db = new myContext(_configSettings);
_db.Database.Initialize(force: true);
_dbResults = new myContext(_configSettings);
// This forces the database to initialise at this point with the initialization data / Empty DB
var count = _db.Accounts.Count();
var resultCount = _dbResults.Accounts.Count();
if (count != resultCount) throw new InvalidOperationException("We do not have a consistant DB experiance.");
}
I use a helper for doing this kind of tasks:
public abstract class TestingHelper
{
public static void ClearDatabase()
{
DatabaseContext myDbContext = new DatabaseContext();
myDbContext.Database.Delete();
myDbContext.Database.Create();
//FillDatabase(lawyers); //<- OPTIONAL if you want to add rows to any type tables
}
}
And then use it in the SetUp of your test:
[SetUp]
public void MyTests_SetUp()
{
TestingHelper.ClearDatabase();
}
Am I correct to think that I have to create my controller by passing it an instance of my context AND my Service to make it testable?
For example: new Controller(mycontext,myservice)
I'm thinking this is the way I need to change my code, but I don't want to if I don't have to. Since for MVC3 to work out of the box it requires controller constructors to be parameterless, I think this means I'm going to have to go down the path of IoC. Otherwise the code in my Wizard action saves to a real DBContext even during testing.
namespace mvc3test.Controllers
{
public class WizardController : Controller
{
private DR405DBContext db;
public WizardController(DR405DBContext dbContext)
{
db = dbContext;
}
public WizardController()
{
db = new DR405DBContext();
}
public ActionResult Index()
{
var model = new WizardViewModel();
model.Initialize();
return View(model);
}
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard)
{
//wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request.QueryString["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Review", wizard);
}
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
public ActionResult Review(int id)
{
var service = new DR405Service();
var dr405 = service.GetDR405ById(db, id);
var wizard = new WizardViewModel();
if (dr405 != null)
{
wizard.Initialize();
foreach (var s in wizard.Steps)
{
Mapper.Map(dr405, s, typeof(dr405), s.GetType());
}
}
return View(wizard);
}
public ActionResult Transmit()
{
return View();
}
[HttpPost]
public String Upload(HttpPostedFileBase FileData)
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TaxPayerID);
System.IO.Directory.CreateDirectory(saveLocation);
FileData.SaveAs(Path.Combine(saveLocation, FileData.FileName));
ViewBag.Message = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", FileData.FileName, (int)FileData.ContentLength / 1024);
return ViewBag.Message;
}
}
}
Am I correct to think that I have to create my controller by passing it an instance of my context AND my Service to make it testable?
Kind of. That's only half of the work you need to do. The second half is to weaken the coupling between your layers by using abstractions. Your service layer needs to implement an interface which you would inject into the constructor of your controller enforcing the contract between those two and explicitly stating that the controller needs a service layer obeying this contract:
public WizardController(IMyService service)
{
this._service = service;
}
Now in your unit test go ahead and mock it using one of the multiple mocking frameworks out there (Rhino Mocks, NSubstitute, Moq, NMock, ...).
You can use setter injection instead of constructor injection on the Controller.
public class WizardController : Controller
{
public void setDBContext( DR405DBContext db) {
this.db = db;
}
}
or
You can get the database using a Service Locator and add a setter to that.
public class DBServiceLocator
{
private static DR405DBContext db = new DR405DBContext();
public static DR405DBContext locate() {
return db;
}
public static setContext(DR405DBContext db) {
DBServiceLocator.db = db;
}
}
In the setup() part of your unit test, use setters to 'inject' your mock/stub database.
Also, using an interface rather than DR405DBContext will make mocking easier.