无法形成Moq测试来欺骗上下文访问

时间:2019-12-22 07:49:03

标签: c# mocking moq

对于Moq来说是完全陌生的,并且通常是模拟测试。我正在尝试遵循一个教程,但适合我的需要,它欺骗了通过entityFrameworkCore上下文进行的某些数据库访问。如何设置和测试我的数据库返回0或任意数字的响应?

为澄清起见,我想测试向我的DoSomething方法提供“ Whatever”将返回0,并且提供任何其他字符串将产生一个条目ID。当然,Id取决于现实生活中的数据库增量,因此我只需要设置一个任意数字作为响应并测试是否返回此数字。当然,这是我真正方法的一个非常小的例子。

我已尽可能减少设置:

接口:

public interface ITestClass
{
    int DoSomething(string thing);
}

实施:

public class TestClass : ITestClass
{
    private readonly TestContext _testContext;
    public TestClass(TestContext testContext)
    {
        _testContext = testContext;
    }
    public int DoSomething(string thing)
    {
        if (thing == "Whatever") return 0;
        Item i = new Item()
        {
            Thing = thing
        };
        _testContext.Add(i);
        _testContext.SaveChanges();
        return i.Id;
    }
}

上下文:

public class TestContext : DbContext
{
    public TestContext(DbContextOptions<TestContext> options) : base(options) { }
}

表/模型类:

public class Item
{
    public int Id { get; set; }
    public string Thing { get; set; }
}

我已经忽略了连接字符串,因为重点是在不连接数据库的情况下测试方法,对吗?最后,这是我对模拟完全没有头绪的尝试,tbh:

    public void Test1()
    {
        var mock = new Mock<ITestClass>();
        mock.Setup(m => m.DoSomething("Whatever"));
        // Assert returns 0
        mock.Setup(m => m.DoSomething("ValidString"));
        // Assert returns arbitrary 12345 - where do I spoof this number?
    }

1 个答案:

答案 0 :(得分:1)

我认为您对如何使用模拟有基本的误解。让我们看看您的测试

public void Test1()
    {
        var mock = new Mock<ITestClass>();
        mock.Setup(m => m.DoSomething("Whatever"));
        // Assert returns 0
        mock.Setup(m => m.DoSomething("ValidString"));
        // Assert returns arbitrary 12345 - where do I spoof this number?
    }

现在,当您在模拟程序上使用安装程序时,还应使用Returns,ReturnsAsync或Throws来告知模拟程序,当您提供这些值时要返回什么。因此,您的DoSomething设置应如下所示:

mock.Setup(m => m.DoSomething(It.Is<string>(i => i == "ValidString"))).Returns(12345);

但是这里应该有一个明显的问题。如果我们明确地告诉它对于给定的输入该给我们什么,那么我们真正在测试什么呢?那时只是模拟本身。这不是要使用模拟程序的方式。因此,请永远不要嘲笑您正在测试的东西。取而代之的是嘲笑其依赖项。在这种情况下,唯一的是TestContext。相反,我们实际上想测试DoSomething方法,因此我们创建了该类的真实实例进行测试。

您没有指定要使用的测试框架,所以我用NUnit编写了我的

        [Test]
        public void Test1()
        {
            // Mock the dependency
            Mock<ITestContext> mockContext = new Mock<ITestContext>();
            mockContext.Setup(m => m.Add(It.IsAny<Item>()))
                .Returns(true);
            mockContext.Setup(m => m.SaveChanges())
                .Returns(true);

            // Inject the dependency
            TestClass testClass = new TestClass(mockContext.Object);

            int result = testClass.DoSomething("Whatever");

            // Verify the methods on the mock were called once
            mockContext.Verify(m => m.Add(It.IsAny<Item>()), Times.Once);
            mockContext.Verify(m => m.SaveChanges(), Times.Once);

            // Assert that the result of the operation is the expected result defined elsewhere
            Assert.That(result, Is.EqualTo(ExpectedResult));
        }

测试模拟是否返回您设置的返回值没有任何价值。我们要测试真实的交易,当依赖关系很复杂时,通过模拟就可以实现。它们是真实系统在实践中使用的后端系统和复杂对象图的替代品,仅此而已。神奇之处在于,它允许我们仅设置所需模拟的各个部分,特定的函数调用和依赖于该类的类所依赖的属性访问,而不必担心设置整个类以及所有其他类。它的依存关系等等。