3

I am testing a repository pattern I have created and I use Moq package to mock my objects. I wanted to test references from 2 objects, but the result kind of surprises me. Here is the test:

Mock<Repository<Web_Documents>> moqRepo;
Mock<Repository<Web_Documents>> moqRepo2;

public void ObjEqGetTest()
{
    //context is DBContext and has been initialized using [TestInitialize] annotation
    moqRepo = new Mock<Repository<Web_Documents>>(context);
    moqRepo2 = new Mock<Repository<Web_Documents>>(context);
    var test = moqRepo.Object.Get(1L);
    var test2 = moqRepo2.Object.Get(1L);
    Assert.AreSame(test, test2);
}

And my Get method returns:

return entities.SingleOrDefault(predicate)

predicate being created using Expression builder (I can add code if needed).

Why does this Assert return true, when I have created two differents objects?

Is it that the Get method returns the same reference whenever you fetch data from a DB (since it points to the model being used)?

Thanks for your help!

EDIT @CodeCaster said Mocking repos will return null in the request I made. But When I verify values in my table Web_Documents, Assertions return true. Let me demonstrate this :

public void IdExistsGetTest()
{
    moqDoc = new Mock<Repository<Web_Documents>>(context);
    var testDoc = moqDoc.Object.Get(1L);
    Assert.AreEqual(testDoc.NomDocument, "Ajouter une catégorie");
}

This test is successful, and in Web_Documents, the row at ID = 1 has NomDocument = "Ajouter une catégorie".

Flexabust Bergson
  • 732
  • 14
  • 34
  • Please read [ask] and create a [mcve]. You also shouldn't depend on a DbContext for unit testing, mock that away. It makes no sense to mock the class under test, you mock dependencies. Both test and test2 will be null, this equal. – CodeCaster Jun 22 '17 at 09:32
  • 1
    I based my answer on a false premise and have removed it. What I claimed is only true when mocking an interface, not when mocking a concrete class. I didn't expect Moq to call into the concrete class when its methods have not been set up. You're still using mocking entirely wrong though, will try to edit the answer. – CodeCaster Jun 22 '17 at 09:58
  • @CodeCaster I do have a question as to what you said. My `Repository` needs a DBContext as parameter, so if I mock only context, how to give a DBContext parameter to my `Repository` when I create it? – Flexabust Bergson Jun 22 '17 at 10:00
  • @CodeCaster Yes I have still much to learn about mocking, this was my first try. – Flexabust Bergson Jun 22 '17 at 10:00
  • 1
    @LéonardLaiter pass that as `new Repository(yourContextMock.Object)` for example. – Evk Jun 22 '17 at 10:11

2 Answers2

4

I assume context is Entity Framework context. You share it between your two repositories, so your two queries on different repositories will return the same entity, because Entity Framework "caches" entities (in certain sense) inside the context. When it sees your query returns entity which is already attached to the context (in this case - returned by first query) - it will return the same entity again for the second one (entities are "same" if they have the same primary key and type).

Evk
  • 98,527
  • 8
  • 141
  • 191
2

Evk already answered why the return values are identical: the DbContext returns the same instance for the same primary key.

I wanted to say something about your code though. Here:

moqRepo = new Mock<Repository<Web_Documents>>(context);
moqRepo2 = new Mock<Repository<Web_Documents>>(context);

You're mocking the class you want to test. You shouldn't mock the class under test, you should mock its dependencies.

So mock the DbContext, and inject that into your non-mocked repos:

var contextMock = new Mock<DbContext>(MockBehavior.Strict);

var repo = new Repository<Web_Documents>(MockBehavior.Strict, contextMock.Object);
var repo2 = new Repository<Web_Documents>(MockBehavior.Strict, contextMock.Object);

Now you'll have to set up the contextMock to return what you want. when called the way it's called by your Get() methods.

See for example Mocking EF DbContext with Moq for more mocking of Entity Framework.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • 1
    I've made a test with this code, and it's not true that `Get` returns null or that `context` is never accessed. Get returns entity as it should, context is accessed, no nulls returned. It's true though that OP should mock DbContext. – Evk Jun 22 '17 at 09:45
  • @CodeCaster One thing though, do I have to Adapt my Repository's constructor so that it accepts `MockBehavior.Strict` ? I have implemented your suggestion but it raises MockException now saying "All invocations onthe mock must have a corresponding setup" even though I add the enumeration to Repository constructor. – Flexabust Bergson Jun 22 '17 at 13:08
  • @Leo no, that parameter instructs Moq that you want to set up every invocation on the mocked class. If you don't want that (but you should), then remove that from the instantiation. – CodeCaster Jun 22 '17 at 13:14
  • @CodeCaster But if I create repos with this as parameter, I get compilation error because my Repository does not accept this parameter. – Flexabust Bergson Jun 22 '17 at 13:17
  • 1
    @Leo it isn't a parameter for your classes, it is for Moq. It instructs Moq that you want to test your code strictly. See https://stackoverflow.com/questions/4996327/moq-strict-vs-loose-usage – CodeCaster Jun 22 '17 at 13:20
  • @CodeCaster Oh ok you put it as parameter in Repository that's why I was confused. – Flexabust Bergson Jun 22 '17 at 13:38