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