C# moq框架的真正目的

C# moq框架的真正目的,c#,unit-testing,nunit,moq,C#,Unit Testing,Nunit,Moq,我被要求使用Moq框架编写单元测试。我对如何用c#编写Moq测试相当陌生 我正在经历这一切 下面是我现在正在做的事情,因为我们正在对存储库使用依赖项注入 //Repository public interface IRepo { IQueryable<MyModel> GetById( long userId ); } public class Repo : BaseManager, IRepo { public Repo(myDbContext conte

我被要求使用Moq框架编写单元测试。我对如何用c#编写Moq测试相当陌生

我正在经历这一切

下面是我现在正在做的事情,因为我们正在对存储库使用依赖项注入

    //Repository
public interface IRepo
{
    IQueryable<MyModel> GetById( long userId );
}

public class Repo : BaseManager, IRepo
{
    public Repo(myDbContext context)
    {
        dbContext = context; //dbContext is from BaseManager class
    } 
    public IQueryable<MyModel> GetById( long userId )
    {
        return dbContext.MyModel.Where(x => x.IsActive && x.UserId == userId );
    }
}

//Test class
public class Test 
{
    Mock<DbSet<MyModel>> mockSet;
    Mock<myDbContext> mockContext;
    Mock<IRepo> mockRepository;

    [TestInitialize]
    public void Setup()
    {
        var data = new List<MyModel>{
            //3 records
        }.AsQueryable();

        var mockSet = new Mock<DbSet<MyModel>>();
        mockSet.As<IQueryable<MyModel>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<MyModel>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<MyModel>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<MyModel>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        var mockContext = new Mock<myDbContext>();
        mockContext.Setup(c => c.MyModel).Returns(mockSet.Object);

        mockRepository = new Mock<IRepo>();
        mockRepository.Setup(m => m.GetById(It.IsAny<long>())).Returns(data); //Here the GetById method is set to return all 3 records set in data object. 
    }

    [TestMethod]
    public void Test_Mock_For_Nothing()
    {
        var controller = new MyController(mockRepository.Object);
        var result = controller.GetById(1); //this will call GetById method in the repository

        Assert.AreEqual(result.Count(), 1);//This will fail as we will get count as 3
    }
}
//存储库
公共接口IRepo
{
IQueryable GetById(长用户ID);
}
公共类回购:BaseManager,IRepo
{
公共回购(myDbContext)
{
dbContext=context;//dbContext来自BaseManager类
} 
公共IQueryable GetById(长用户ID)
{
返回dbContext.MyModel.Where(x=>x.IsActive&&x.UserId==UserId);
}
}
//测试班
公开课考试
{
模拟模拟集;
模拟语境;
模拟模拟存储库;
[测试初始化]
公共作废设置()
{
var数据=新列表{
//3项记录
}.AsQueryable();
var mockSet=new Mock();
mockSet.As().Setup(m=>m.Provider).返回(data.Provider);
mockSet.As().Setup(m=>m.Expression).Returns(data.Expression);
mockSet.As().Setup(m=>m.ElementType).Returns(data.ElementType);
mockSet.As().Setup(m=>m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext=new Mock();
Setup(c=>c.MyModel).Returns(mockSet.Object);
mockRepository=newmock();
mockRepository.Setup(m=>m.GetById(It.IsAny())).Returns(data);//这里GetById方法设置为返回数据对象中设置的所有3条记录。
}
[测试方法]
公共无效测试
{
var controller=newmycontroller(mockRepository.Object);
var result=controller.GetById(1);//这将调用存储库中的GetById方法
Assert.AreEqual(result.Count(),1);//这将失败,因为我们将得到计数为3
}
}
因此,尽管我模拟了上下文和存储库,但存储库中的逻辑永远不会执行。因为GetById方法将根据伪数据直接返回带有3条记录的结果

我希望根据repository方法中的逻辑过滤出虚拟数据。最低起订量可以吗


当存储库代码未被执行时,使用Moq框架的真正目的是什么?

通过单元测试,您可以尝试测试/强化业务逻辑。在您的存储库中不应该有任何BL(Businesslogic)。使用您的模拟框架,您认为您不想进行测试。->Repository=访问数据。 有了它,您可以改变获取数据(存储库)的方式,而无需对Businesslogic/UnitTests进行任何更改:)


PS:如果你想测试你的存储库或更多,你应该以集成测试甚至端到端测试为目标。

就你的测试而言,我认为使用模拟框架甚至进行此测试都没有什么好处。我的想法是:

1) 如果您有一个存储库,请编写一个集成测试,以证明您正在从数据库/web服务等返回数据


2) 在您想要测试某些业务逻辑/行为的情况下使用Moq或Rhinomock

鉴于您已经模拟了存储库,但没有看到控制器逻辑,我不确定您为什么需要模拟dbContext。但是通过这个测试,您实际上是在测试控制器逻辑,而不是存储库,因为您模拟了存储库以及存储库中的GetById返回的内容

如果您希望在存储库中测试过滤器逻辑,则需要模拟dbContext(正如您所做的那样),并在测试中创建一个新的具体存储库实例,并测试从调用GetById返回的数据

因此,您模拟dbContext.MyModel,返回三项,并让Where调用执行过滤


关于模拟框架的使用,有很多有用的信息,但是每个人对于应该测试什么以及应该在单个单元测试中测试的小单元都有一些不同的看法,经验和个人偏好是这里的关键。

你在哪里给出
mockRepository
mockContext
?你为什么要模仿上下文,而你已经模仿了存储库。嘲弄的本质是说,除了我使用的这一种方法,忘掉一切,
这种方法需要给我这些数据。在这些情况下,无需担心上下文。@Callumlington我需要数据流经存储库并返回有效的项目列表。因此,我在模拟上下文,我在运行单元测试之前设置期望值(使用
.Returns
方法)。所以,我知道考试会通过的。当我知道一切都能正常工作时,嘲弄一切又有什么意义呢?你知道这可以使用IQueryable,这很好,但是当它与SQL对话时,LINQ中的某些命令将不可用。因此,对存储库端进行集成测试总是更好的?我指的是
mockRepository.Setup
进行控制器级测试是有效的,如果您模拟了存储库,那么您的测试表明控制器直接从存储库返回。这似乎毫无意义,但如果将来有人修改控制器逻辑并破坏了它,那么您就知道控制器逻辑不再只是从存储库返回数据。我认为您还需要一个存储库测试,在这个存储库测试中,您可以测试您的过滤逻辑。因此,在控制器测试中,只需模拟存储库,在存储库测试中,只需模拟db contextI将在Moq.dll中将模拟的上下文传递给具体的repo构造函数时抛出异常:'Castle.DynamicProxy.InvalidProxyConstructorArgumentsException'。有什么想法吗?很可能是因为你在模仿一些不是接口的东西,你可以为你的数据库上下文创建一个接口,然后将它注入到你的存储库中