2

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.

frontsidebus
  • 120
  • 1
  • 9
  • I don't see `Table` anywhere in your example? – Adam Ralph Feb 21 '13 at 12:07
  • @AdamRalph JustMock's ReturnsCollection allows you to return an enumerable for a given linq query http://goo.gl/VBuPP. In this event, the tester doesn't need to return a Table in the arrange method http://goo.gl/45HC7 – cecilphillip Feb 21 '13 at 19:52
  • Also, FakeItEasy uses DynamicProxy from Castle which can't intercept non-virtual methods – cecilphillip Feb 22 '13 at 20:26

2 Answers2

2

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.

cecilphillip
  • 11,446
  • 4
  • 36
  • 40
2

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.

cecilphillip
  • 11,446
  • 4
  • 36
  • 40
Adam Ralph
  • 29,453
  • 4
  • 60
  • 67
  • This is a much more elegant solution than my own. Yet, using an explicit interface implementation still seems a little dirty – cecilphillip Apr 02 '13 at 20:28
  • It's not ideal ;-) The Linq2Sql codegen should generate an interface itself. Quite ridiculous that it doesn't, IMHO. If the designers of it had given just a little thought with regard to unit testing then they would have added this in the first place. – Adam Ralph Apr 03 '13 at 06:44
  • It's a very similar situation with EntityFramework and IDbSet too isn't it? It would be great if FakeItEasy handled these collection cases for you like JustMock does. – cecilphillip Apr 03 '13 at 20:11
  • Luckily I've managed to avoid EF so far in my career ;-) Is it another problem of a non-virtual method on a context object which doesn't implement an interface? I guess commercial tools like JustMock allow you to mock out such methods (and statics) but with a well designed API that just shouldn't be necessary. Sadly, the world is full of badly designed APIs :-( – Adam Ralph Apr 04 '13 at 06:09
  • I agree it is sad they didn't give it that thought. – frontsidebus May 11 '13 at 16:39