简体   繁体   中英

Why are the DbSets in my mocked context null?

Recently, I've been trying to study unit tests and mocking in c#. For my tools of choice, I have been using Nunit as my testing framework, AutoFac as my IoC Container, Moq for mocking DBs, and Entity Framework 6 for speaking with said DBs.

During my unit test to perceive if I could successfully load from a mock database, I was receiving a NullReferenceException - The object I was looking for could not be found in my mock database.

By stepping through my test, I was able to determine that my mockSet, as well as my MockContext both indeed have the test record I set within them.

Automock was also able to successfully create an object of the class I am testing, and pass it an instance of the context the tested method relies upon. However, the DbSets within this instance of the context all display as null, leading to my Find() function to fail. 图像显示数据根实现中的 Character DbSet 为空。

I am uncertain as to why the DbSet of Characters within my implementation is null. As I understand it, after creating my mockSet and mockContext, AutoMock should detect and inject my mockContext into anything that calls for my context during this unit test. Is that not the case? If so, what is the case? What step do I take to ensure that the class I am testing is injected with an instance of my mockContext?

Here is my unit test:

[Test]
public void MySqlDataRoot_LoadCharacterByCharacterID_SingularCharacterObjectReturned()
{
    using (var mock = AutoMock.GetLoose())
    {
        //Attain
        //When my code that I'm testing calls for a CharacterContext, return the mock instead.
        //Currently, this mock only has a table for Characters set up. trying to get other tables will fail.

        //1. Create data to be set in the mock set.
        //Tested and confirmed that my test record is being placed into my mockSet and mockContext properly.
        List<Character> charList = new List<Character>();
        charList.Add(getSampleCharacter());
        IQueryable<Character> data = charList.AsQueryable();

        //2. Create mock set.
        var mockSet = new Mock<DbSet<Character>>();
        mockSet.As<IQueryable<Character>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Character>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Character>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Character>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        //3. Create a context based upon the mockSet
        var mockContext = new Mock<CharacterContext>();
        mockContext.Setup(c => c.Characters).Returns(mockSet.Object);

        //Act
        //DataRoot is created and has a _characterContext, but the Characters DbSet is null.
        var dataRoot = mock.Create<MySqlDataRoot>();
        var expected = getSampleCharacter();
        var actual = dataRoot.GetCharacterBy_CharacterID(Guid.Parse("11111111-2222-3333-4444-555555555555"));

        //Assert
        Assert.True(actual != null);
        Assert.AreEqual(expected.Character_id, actual.Character_id);        
    }
}

I think the problem is that the mock CharacterContext you create at step 3 is not the same one that the mock MySqlDataRoot has. I don't see anything that would ensure that it is the same instance. Then again, I don't use the automocking features, so I'm not familiar with how it usually works.

Found the solution, with thanks to Plasmadog for pointing out my error, as well as Shafiq Jetha's answer here .

The problem I had was twofold: Firstly, I was not using the same mockContext as I thought I had been passing, as Plasmadog points out in his answer.

Secondly, I was missing the EntityFrameworkTesting.Moq package, as well as some critical functions that were pointed out by Shafiq.

For anyone who finds this answer in the future, here is the solution that worked for me.

[Test]
public void MySqlDataRoot_LoadCharacterByCharacterID_SingularCharacterObjectReturned()
{

    //Arrange
    //1. Create the test data.
    List<Character> charList = new List<Character>();
    charList.Add(getSampleCharacter());

    //2. Create a mock set, one that properly responds to EntityFramework's .Find()
    var mockSet = new Mock<DbSet<Character>>()
        .SetupData(charList, o =>
        {
            return charList.Single(x => x.Character_id.CompareTo(o.First()) == 0);
        });

    using (var mockContext = AutoMock.GetLoose())
    {
        //Act
        //3. Use the mockSet to properly create the mockContext.
        mockContext.Mock<CharacterContext>().Setup(x => x.Characters).Returns(mockSet.Object);

        //4. Create a instance of MySqlDataRoot, injecting mock via the constructor.
        IDataRoot toTest = mockContext.Create<MySqlDataRoot>();
        var expected = getSampleCharacter();
        var Actual = toTest.GetCharacterBy_CharacterID(expected.Character_id);


        //Assert
        Assert.IsNotNull(expected);
        Assert.IsNotNull(Actual);
        Assert.AreEqual(Actual.Character_id, expected.Character_id);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM