3
// acknowledgement: http://stackoverflow.com/a/5022512/1500199
public class FakeDbSet<T> : IDbSet<T> where T : class
{
    private readonly HashSet<T> data;
    private readonly IQueryable query;

    public FakeDbSet()
    {
        data = new HashSet<T>();
        query = data.AsQueryable();
    }

    public virtual T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }
}

How can I implement Find?

I need to determine the primary key value of T in order to perform a key value comparison in the Find method but I do not know how.

Caster Troy
  • 2,796
  • 2
  • 25
  • 45
  • Could it be that I need to make each entity implement an interface such as `IEntity` that has a property `IEntity.Key` and then enforce a generic constraint `where T : IEntity` for `FakeDbSet`? – Caster Troy Jul 26 '14 at 16:45
  • 1
    Can you add dbContext instance in the stub class ? – Yuliam Chandra Jul 26 '14 at 17:07
  • @YuliamChandra I do not understand. – Caster Troy Jul 26 '14 at 17:28
  • @CasterTroy, if you have an instance a DbContext, there are methods allowing you to retrieve the keys of a given entity. And once you know which are the key properties you could simply look for those entities in your fake `HashSet`. But I guess you don't have an instance of a DbContext because from what I understand the purpose of this class is to mock the database itself. – Darin Dimitrov Jul 26 '14 at 17:30
  • adding dbcontext to the test class might not appropriate, just to show you [this link](http://stackoverflow.com/questions/6906753/how-to-get-the-primary-keys-in-entity-framework-4-1-i-e-using-dbcontext) to get the primary key property names, then from there you can get the values from reflection.. – Yuliam Chandra Jul 26 '14 at 17:31
  • @DarinDimitrov I do not. I expose each `IDbSet` via an interface. – Caster Troy Jul 26 '14 at 17:32
  • 1
    Then the only way to achieve that is what you've already suggested in your first comment. Make your entities implement an interface indicating which is the Key property. But things can get tricky if you have entities with complex primary keys. – Darin Dimitrov Jul 26 '14 at 17:36

3 Answers3

2

Try this:

public class FakeDbSet<T> : IDbSet<T> where T : class
{
    private Func<T, object[], bool> _findSelector
    private readonly HashSet<T> data;
    private readonly IQueryable query;

    public FakeDbSet(Func<T, object[], bool> findSelector)
    {
        _findSelector = findSelector;
        data = new HashSet<T>();
        query = data.AsQueryable();
    }

    public virtual T Find(params object[] keyValues)
    {
        return _data.SingleOrDefault(item => _findSelector(item, keyValues));
    }
}
Keith Payne
  • 3,002
  • 16
  • 30
  • Thanks, this answers the question. However, I am not going to actually use this - It is gross. Instead I am going to adapt a new design strategy. – Caster Troy Jul 26 '14 at 20:40
1

If I understood you properly You need to stub the method Find() of the DbSet object for unit testing purposes.

If you are using EntityFramework, which has version 6 or higher and using Mocking Frameworks such as Moq or NSubstitute or FakeItEasy you can achieve this easily without writing classes like your FakeDbSet.

This article provides full explanation of stubbing/mocking process: http://msdn.microsoft.com/en-us/data/dn314429.aspx

And an example using classes from this article.

    [Test]
    public void Foo_WhenCalled_CallsDb()
    {
        //Arrange
        var fakeDbSet = new Mock<DbSet<Course>>();
        fakeDbSet.Setup(dbs => dbs.Find(It.IsAny<object>())).Returns(new Course { CourseID = 125 });

        var fakeContext = new Mock<SchoolEntities>();
        fakeContext.Setup(c => c.Courses).Returns(fakeDbSet.Object);


        var foo = new Foo(fakeContext.Object);

        //Act
        string result = foo.MyMethod();

        //Assert
        Assert.AreEqual("125", result);
    }

For other versions of EntityFramework consider using "Repository" pattern - it provides great testing capabilities. I hope my answer will help you.

prime_z
  • 544
  • 6
  • 9
  • Thanks. There is no way you could have known this but this is not suitable for me as I am working against a populated fake repository already. – Caster Troy Jul 26 '14 at 20:41
0

Override the method

public class FakePostsDbSet : FakeDbSet<Post>
{
    public override Post Find(params object[] keyValues)
    {
        return this.SingleOrDefault(
            post => post.Slug == (string) keyValues.Single());
    }
}
Caster Troy
  • 2,796
  • 2
  • 25
  • 45