Reputation: 4811
Any better examples or tutorials available on Unit testing projects using Entity framework than this
In my case API project is using Entity framework file Edmx file and accessing the tables from the edmx file from Repository class. [ Not really like the codefirst or dbfirst approach ]
The structre of repo class looks like
public class AppBackendRepository
{
// modify the type of the db field
private AppDBEntities db_context = new AppDBEntities();
public List<Student> Get()
{
return db_context.Students.ToList();
}
}
public class StudentController
{
private static AppBackendRepository repo;
public StudentController()
{
repo = new AppBackendRepository();
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = repo.Get();
return students;
}
}
How can i write a proper Unit testing against this way of code architecture
Upvotes: 0
Views: 2721
Reputation: 10851
I'm of the opinon that all your code should be tested. This way you can easily detect when some developer breaks an expected chain. Because of that I always add tests for both repositories and controllers. In your case I would add a test that ensures that your controller uses your repository in a correct way, and that your repository uses EF the right way. However, you should not test EF itself. That's Microsofts problem.
First you must abstract the DbContext.
public class YourContext : DbContext, IDbContext
{
public virtual IDbSet<Student> Students { get; set; }
}
public interface IDbContext
{
IDbSet<Student> Students;
}
// Util for creating a testable context.
public class ContextUtils
{
internal static IDbSet<T> GetMockDbSet<T>(IEnumerable<T> data) where T : class
{
IQueryable<T> queryable = data.AsQueryable();
IDbSet<T> dbSet = MockRepository.GenerateMock<IDbSet<T>, IQueryable>();
dbSet.Stub(m => m.Provider).Return(queryable.Provider);
dbSet.Stub(m => m.Expression).Return(queryable.Expression);
dbSet.Stub(m => m.ElementType).Return(queryable.ElementType);
dbSet.Stub(m => m.GetEnumerator()).Return(queryable.GetEnumerator());
return dbSet;
}
public static IDbContext GetMockDbContext()
{
var dbContext = MockRepository.GenerateMock<IDbContext>();
dbContext.Stub(x => x.Student).PropertyBehavior();
dbContext.Students = GetMockDbSet(GetStudents());
return dbContext;
}
private static IEnumerable<Student> GetStudents()
{
// Create some mock data.
return new List<Student>
{
new Student()
{
StudentID = 1,
Name = "Student One",
},
new Student()
{
StudentID = 2,
Name = "Student Two",
},
new Student()
{
StudentID = 3,
Name = "Student Three",
}
};
}
}
Now you have a DbContext that can be tested. More information regarding the mocking of DbContext can be found on this blog.
http://aikmeng.com/post/62817541825/how-to-mock-dbcontext-and-dbset-with-moq-for-unit
Then make sure that you can test your repository.
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
return _dbContext.Students.ToList();
}
}
It can also be done with a factory.
public class AppBackendRepository
{
public List<Student> Get()
{
using (var context = DbContextFactory.GenerateContext())
{
return context .Students.ToList();
}
}
}
public interface IDbContextFactory
{
/// <summary>
/// Creates a new context.
/// </summary>
/// <returns></returns>
IDbContext GenerateContext();
/// <summary>
/// Returns the previously created context.
/// </summary>
/// <returns></returns>
IDbContext GetCurrentContext();
}
public class DbContextFactory : IDbContextFactory
{
private IDbContext _context;
public IDbContext GenerateContext()
{
_context = new DbContext();
return _context;
}
public IDbContext GetCurrentContext()
{
if (_context == null)
_context = GenerateContext();
return _context;
}
}
Now you can test the repository and make sure that it's using EF the right way.
[TestMethod]
public void ShouldReturnAllValues()
{
int correctAmount = 3; // The number specified in MockUtils.
var dbContext = MockUtils.GetMockDbSet();
var repo = new AppBackendRepository(dbContext);
var result = repo.Get();
Assert.IsTrue(result.Count() == correctAmount);
}
What you actually tested is that no developer broke the intended code with something like:
public class AppBackendRepository
{
private IDbContext _dbContext;
// With injection.
public AppBackendRepository(IDbContext context)
{
_dbContext = context;
}
public List<Student> Get()
{
// Only active...
return _dbContext.Students.Where(x => x.Active).ToList();
}
}
Now that you know that the repo is doing what it's supposed to, you can simply make sure that your controller is calling the repo and actually returns the value.
public class StudentController
{
private static IAppBackendRepository _repo;
public StudentController(IAppBackendRepository repo)
{
_repo = repo;
}
public IEnumerable<Student> GetStudents()
{
List<Student> students = _repo.Get();
return students;
}
}
[TestMethod]
public void ShouldCallRepo()
{
// With Rhino
var mockRepo = MockRepository.GenerateStub<IAppBackendRepository>();
var expectedResult = new List<Student>();
mockRepo.Expect(x => x.Get()).Return(expectedResult);
var controller = new StudentController(mockRepo);
var actualResult = controller.GetStudents();
mockRepo.VerifyAllExpectations();
Assert.AreEqual(actualResult, expectedResult); // Possible in it's own method.
}
What you actually tested here is that your controller doesn't manipulate the list before returning it, and that it's actually using the repo as intended.
Also, you might consider using an IoC like Structuremap or Unity. It makes it much easier to make testable applications.
Upvotes: 1
Reputation: 3222
The quick answer is: You don't. Now, I say this because I tend to regard "Unit Tests" as something that is quick and can be used in continuous integration, while "Integration tests" are the slow tests that only run at night and, of course, when you're working with them.
The problem you're creating here is that you're using untestable code.
Take your method, "GetStudents()" as an example. You're depending on the repo to actually exist before calling this method. Any unit-test will depend on Entity Framework being installed, AND of course, this will be super-slow. Imagine a few hundred of these, and your unit test framework is now a serious clog in your system that makes people say "IT's so slow that we don't use it"
A better approach would be to implement the Dependency Inversion Principle
First, define an interface:
public interface IStudentRepository
{
IEnumerable<Student> GetStudents();
}
Now, your class is just an implementation detail of that contract, for example:
public class StudentRepository : DbContext, IStudentRepository
{
private DbSet<Student> Students;
public IEnumerable<Student> GetStudents()
{
return Students;
}
}
In the class that uses your repository, you can now inject your instance by constructor injection, and end up with something that is fully unit-testable:
public class StudentEnrollment
{
private readonly IStudentRepository _studentRepository;
// Inject the contract here
public StudentEnrollment(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
public IEnumerable<Student> GetStudentsForClass(StudentClass studentClass)
{
return _studentRepository.GetStudents().Where(student => student.class == studentClass);
}
}
And now, as the added bonus, you can Unit-Test every last bit of logic, for example:
[TestMethod]
public void GetStudentsForClass_GetStudentsThrowsException_ResultIsNull()
{
// Arrange
var mock = Mock.Create<IStudentRepository();
var badException = new Exception("I'm bad");
mock.Setup(repo => repo.GetStudents()).Throws(badException);
var someClass = new StudentClass();
var instance = new StudentEnrollment(mock.object);
// Act
var result = instance.GetStudentsForClass(studentClass);
// Assert
result.ShouldBeEmpty();
}
Upvotes: 2