I am confused whether I should do TDD on Repository at all or not. I understand that it is not doing any major operations/ business functionality. However, many are doing testing on Repository as well.
I can understand that it is then actually an Integration testing where the data will be hit directly to the database.
My GenericRepository has got one Insert method:
public virtual bool Insert(TEntity entity)
{
var validationResult = this.context.Entry(entity).GetValidationResult();
if (validationResult.IsValid)
{
this.databaseSet.Add(entity);
return true;
}
else
{
var errors = new List<DbEntityValidationResult>();
errors.Add(validationResult);
throw new DbEntityValidationException(string.Empty, errors);
}
}
Now I want to test this method using Moq. The TestInitializer looks like as follows:
[TestInitialize]
public void Initialize()
{
// Arrange.
this.MockData = new List<Document>
{
new Document { },
}.AsQueryable();
this.MockSet = new Mock<DbSet<Document>>();
this.MockSet.As<IQueryable<Document>>().Setup(m => m.Provider).Returns(this.MockData.Provider);
this.MockSet.As<IQueryable<Document>>().Setup(m => m.Expression).Returns(this.MockData.Expression);
this.MockSet.As<IQueryable<Document>>().Setup(m => m.ElementType).Returns(this.MockData.ElementType);
this.MockSet.As<IQueryable<Document>>().Setup(m => m.GetEnumerator()).Returns(this.MockData.GetEnumerator());
var entity = new Mock<DbEntityEntry<Document>>();
this.MockContext = new Mock<IDbContext>();
this.MockContext.Setup(m => m.Set<Document>()).Returns(this.MockSet.Object);
this.Repository = new DocumentRepository(this.MockContext.Object);
}
My Test Insert is as follows:
[TestMethod]
public void InsertDocument()
{
var document = new Document()
{
Binary = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
};
Assert.IsTrue(this.Repository.Insert(document));
var exceptionMessage = string.Empty;
Assert.IsTrue(this.Repository.Save(out exceptionMessage));
}
Why I want to do a test on repository?
The reason I want is because it has got validate functionality that I can check against the model.
Where is the problem?
I cannot get pass through this.context.Entry(entity) of Insert function, because it returns null.
Even if I have tried to setup mock with
var dbEntity = this.MockContext.Object.Entry(document);
this.MockContext.Setup(m => m.Entry(It.IsAny())).Returns(dbEntity);
it returns NULL exception.
Please advise, if it is just as good as leave the repsitory testing.
There is no need to test repository alone (with mocked database gateway) because repository is just returning ARs (aggregate roots), and ARs should be created with a factory, so better just test factory if you need to.
Testing repository has sense if it's an integration test, so with an actual working database (simple repository implementation that works fast, ie. sqlite).
Remember about testing pyramid
Related
I am trying to get to grips with TDD, I have reviewed some tutorials and am trying to implement tests on my validation classes that I have created using fluent validation.
public SomeFormValidator()
{
RuleFor(x => x.MyClass).NotNull()
.WithMessage("MyClass cannot be null");
}
I have looked at the TDD examples specifically for fluent validation and created a couple of tests
[Test]
public void Should_have_error_when_MyClass_is_null()
{
MyClass myClass = null;
SomeFormValidator.ShouldHaveValidationErrorFor(aup => aup.MyClass, myClass);
}
[Test]
public void Should_not_have_error_when_MyClass_is_not_null()
{
MyClass myClass = new MyClass();
SomeFormValidator.ShouldNotHaveValidationErrorFor(aup => aup.MyClass, myClass);
}
I would like to now test that the string "MyClass cannot be null" is returned when it is null. I have not been able to find anything covering returned message and I have not been able to work it out.
Thanks to the guidance of #Surgey I was able to come up with a solution that uses the fluent validation built in methods, in addition to that I have been able to better layout my test which I have added below
using FluentValidation.TestHelper;
using NUnit.Framework;
using MyProject.Models...
using MyProject...
namespace MyProject.Tests.Classes.Validation
{
[TestFixture]
public class SomeFormValidatorTest
{
private SomeFormValidator SomeFormValidator;
[SetUp]
public void Setup()
{
SomeFormValidator = new SomeFormValidator();
}
[Test]
public void Should_display_correct_error_message_MyClass_is_null()
{
//arrange
MyClass myClass = null;
//act
var result = SomeFormValidator.ShouldHaveValidationErrorFor(x => x.MyClass, myClass);
//assert
result.WithErrorMessage("MyClass is required");
//first attempt prior to finding WithErrorMessage exists
//foreach (var r in result)
//{
// Assert.AreEqual("MyClass is required", r.ErrorMessage);
//}
}
}
}
I am using result.WithErrorMessage as that is was is provided in the result but I have left the foreach in, but commented, as I find the error message produced by using Assert.AreEqual produce a better message in the test runner.
There is Arrange-Act-Assert (AAA) technique that helps to structure unit tests properly.
Arrange
First of all, you need to create a System Under Test (SUT) and inputs. In this case that are SomeFormValidator and SomeForm instances:
// arrange
var sut = new SomeFormValidator();
var someFormWithNullProp = new SomeForm { MyClass = null };
Act
Then you need to call the SUT to perform real work. For validators, that is the Validate() method call:
// act
ValidationResult result = sut.Validate<SomeForm>(someFormWithNullProp);
Assert
The last part of the unit test checks if the actual result matches the expectations:
// assert
Assert.False(result.IsValid);
Assert.AreEqual(
"MyClass cannot be null",
result.Errors.Single().ErrorMessage);
I have an ASP.NET MVC4 Web API project with an ApiController-inheriting controller that accepts an ODataQueryOptions parameter as one of its inputs.
I am using NUnit and Moq to test the project, which allow me to setup canned responses from the relevant repository methods used by the ApiController. This works, as in:
[TestFixture]
public class ProjectControllerTests
{
[Test]
public async Task GetById()
{
var repo = new Mock<IManagementQuery>();
repo.Setup(a => a.GetProjectById(2)).Returns(Task.FromResult<Project>(new Project()
{
ProjectID = 2, ProjectName = "Test project", ProjectClient = 3
}));
var controller = new ProjectController(repo.Object);
var response = await controller.Get(2);
Assert.AreEqual(response.id, 2);
Assert.AreEqual(response.name, "Test project");
Assert.AreEqual(response.clientId, 3);
}
}
The challenge I have is that, to use this pattern, I need to pass in the relevant querystring parameters to the controller as well as the repository (this was actually my intent). However, in the case of ODataQueryOptions-accepting ApiController methods, even in the cases where I would like to use just the default parameters for ODataQueryOptions, I need to know how to instantiate one. This gets tricky:
ODataQueryOptions does not implement an interface, so I can't mock it directly.
The constructor requires an implementation of System.Web.Http.OData.ODataQueryContext, which requires an implementation of something implementing Microsoft.Data.Edm.IEdmModel, for which the documentation is scarce and Visual Studio 2012 Find References and View Call Hierarchy do not provide insight (what implements that interface?).
What do I need to do/Is there a better way of doing this?
Thanks.
Looks like someone else already answered this in the comments here, but it's not a complete solution for my use-case (see comment below):
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Customer>("Customers");
var opts = new ODataQueryOptions<Customer>(new ODataQueryContext(modelBuilder.GetEdmModel(),typeof(Customer)), request);
This is the solution I have been using in my NUnit tests to inject ODataQueryOptions
private static IEdmModel _model;
private static IEdmModel Model
{
get
{
if (_model == null)
{
var builder = new ODataConventionModelBuilder();
var baseType = typeof(MyDbContext);
var sets = baseType.GetProperties().Where(c => c.PropertyType.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>));
var entitySetMethod = builder.GetType().GetMethod("EntitySet");
foreach (var set in sets)
{
var genericMethod = entitySetMethod.MakeGenericMethod(set.PropertyType.GetGenericArguments());
genericMethod.Invoke(builder, new object[] { set.Name });
}
_model = builder.GetEdmModel();
}
return _model;
}
}
public static ODataQueryOptions<T> QueryOptions<T>(string query = null)
{
query = query ?? "";
var url = "http://localhost/Test?" + query;
var request = new HttpRequestMessage(HttpMethod.Get, url);
return new ODataQueryOptions<T>(new ODataQueryContext(Model, typeof(T)), request);
}
here's my controller
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
here's my unit test:
[Test]
public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
{
// Arrange
const string expectedRouteName = "~/Views/Home/Index.cshtml";
var registeredUser = new UserRegisterViewModel { Email = "newuser#test.com", Password = "123456789".Hash()};
var confirmation = new ActionConfirmation<User>
{
WasSuccessful = true,
Message = "",
Value = new User()
};
_userService.Setup(r => r.AddUser(new User())).Returns(confirmation);
_accountController = new AccountController(_userService.Object);
// Act
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
}
Unit test failed right here.
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."
I think its because configuration is in web project, not the unit test project. what should I do to fix it?
You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.
You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.
AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).
class MyRegistry : Registry
{
public MyRegistry()
{
For<IMyRepository>().Use<MyRepository>();
For<ILogger>().Use<Logger>();
Mapper.AddProfile(new AutoMapperProfile());
For<IMappingEngine>().Use(() => Mapper.Engine);
}
}
You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap
Probably it is cool to abstract mapping into MappingEngine.
Sometimes I use following approach to IOC Automapper
In IOC builder:
builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();
where GetAutoMapper is:
public class AutoMapperConfiguration
{
public static IMapper GetAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderModelMapperProfile>();
cfg.AddProfile<OtherModelMapperProfile>();
//etc;
});
var mapper = config.CreateMapper();
return mapper;
}
}
And finally in Controller ctor
public MyController(IMapper mapper)
{
_mapper = mapper;
}
I am trying to learn how to unit test and work with MVC 3 and I am getting stuck on the problem of How do I test two models. Here is the code
public class HomeController : Controller
{
private IRepository _repository;
public HomeController(IRepository repository)
{
_repository = repository;
}
//
// GET: /Home/
public ActionResult Index()
{
return View(_repository.GetAllGenres());
}
}
public interface IRepository
{
IEnumerable<Genre> GetAllGenres();
IEnumerable<Album> GetTopAlbums(int count);
}
and assume this is my Unit Testing
[TestFixture]
class HomeControllerTests
{
[Test]
public void Test1()
{
//Arrange
var controller = new HomeController(new InMemoryRepository());
var result = (ViewResult) controller.Index();
Assert.AreEqual(10,((IEnumerable<Genre>)result.ViewData.Model).Count());
}
[Test]
public void Test2()
{
var controller = new HomeController(new InMemoryRepository());
var result = (ViewResult) controller.Index();
//I Want to be able to do something like this
Assert.AreEqual(5,result.ViewData.Model.GetTopAlbums(5).Count);
}
}
Now my question is How exactly do I go about making something like I want work. Or do I create a ChildActionOnly Method that is responsible for returning the Top Albums.
Have you considered using a mocking framework to assist with you tests? For example you could make use of mock on your GetTopAlbums call. My preference is moq but there are several great mocking frameworks to choose from.
Note, this is a pretty simple example but you could easily create a test helper to generate a list with your expected number of albums:
[Test]
public void Index_Get_Should_Lookup_Top_Albums_And_Return_Index_View()
{
// arrange
var expectModel = new List<Album>
{
new Album{Artist= "joe", Tracks = 16},
new Album{Artist= "doe", Tracks = 23},
};
_repository.Setup(x => x.GetTopContacts(It.IsAny<int>())).Returns(expectModel);
var controller = new HomeController(_repository.Object);
// act
var result = controller.TopContacts();
var model = result.ViewData.Model as IEnumerable<Album>;
// assert
Assert.AreEqual(2, model.Count());
}
I have a very good solution for you. See the below two blog posts:
Generic Repository Pattern - Entity Framework, ASP.NET MVC and Unit
Testing Triangle
How to Work With Generic Repositories on ASP.NET MVC and Unit Testing
Them By Mocking
Basically, it is what #Jesse already suggested but those two posts also has some other features there which might be helpful for you.
As the title suggests I'm trying to test a method, unfortunately I appear to be going wrong some where. The method should only return customers that have and ID = 1
Here is my test
[TestMethod]
public void Returns_Correct_Number_Of_Workout_Dates_For_Valid_UserId()
{
//Arrange
List<GymSession> gymSessions = new List<GymSession>();
Customer cust = new Customer();
cust.CustomerId = 1;
gymSessions.Add(new GymSession() { Customer = cust, SessionId = 1, Date = new DateTime(2010, 1, 1) });
gymSessions.Add(new GymSession() { Customer = cust, SessionId = 2, Date = new DateTime(2010, 1, 2) });
gymSessions.Add(new GymSession() { SessionId = 3, Date = new DateTime(2010, 1, 3) });
gymSessions.Add(new GymSession() { Customer = cust, SessionId = 4, Date = new DateTime(2010, 1, 4) });
var mockRepos = new Moq.Mock<IGymSessionRepository>();
mockRepos.Setup(g => g.GymSession()).Returns(gymSessions.AsQueryable());
//Act
var result = mockRepos.Object.GetWorkoutDatesByCustomerId(1);
//Assert
Assert.AreEqual(3, result.Count());
}
Here is the repository method I'm trying to test
public IQueryable<GymSession> GetWorkoutDatesByCustomerId(int userId)
{
var gymSess = db.GymSessions.Where<GymSession>(g => g.Customer.CustomerId == userId);
return gymSess;
}
The idea is that setup has all the customers, and the method then filters them. The count never seems to apply the filter. Any ideas?
It appears that you really want to stub the call to db.GymSessions and that you should test a concrete GymSessionRepository instance. Traditionally, there are two ways to do this (apart from intercepting calls using aspect-oriented programming):
1) Give your repository an explicit dependency on db and require it in the repository constructor. Here's what I mean, where I'm using IDatabase to represent db:
public class GymSessionRepository: IGymSessionRepository {
private IDatabase db;
public GymSessionRepository(IDatabase db) {
this.db = db;
}
}
// Then in your test ...
var mockDb = new Moq.Mock<IDatabase>();
mockDb.Setup(d => d.GymSessions()).Returns(gymSessions.AsQueryable());
GymSessionRepository repository = new GymSessionRepository(mockDb);
// ... and so on
2) (Less desirable, but sometimes necessary) Expose the method you want to stub as a virtual member, mock the concrete object you're testing, and stub the behavior directly on the class under test:
public class GymSessionRepository {
// Because this is virtual, you can override it in your mock object
protected virtual List<GymSession> GymSessions() {
return this.db.GymSessions.AsQueryable();
}
}
// In your test code here: notice the mock object is your concrete class,
// because your test targets another method on that class, not a method
// on an arbitrary implementation (like a mock based on its interface)
var mockRepos = new Moq.Mock<GymSessionRepository>();
// Override its virtual method; take control for your test
mockRepos.Setup(g => g.GymSessions()).Returns(gymSessions.AsQueryable());
Depending on the mocking framework, the second technique is known as using a transparent or partial mock. If you find yourself using it often, it may be a sign that your code is overly-coupled (and it can get confusing fast).