سؤال

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top