You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FakeEasy在.NET中Mock控制器构造参数时遇错求助

问题:FakeEasy模拟ApplicationDbContext时抛出FakeCreationException

我是.NET新手,给TagController写单元测试时,用FakeEasy模拟依赖的ApplicationDbContext,抛出了FakeCreationException。后来发现指定构造参数的写法能正常运行,想知道原因。


测试类代码

public class TagControllerTest
{
    private TagController _tagController;
    private ApplicationDbContext _applicationDbContextMock;

    public TagControllerTest()
    {
        _applicationDbContextMock = A.Fake<ApplicationDbContext>();

        _tagController = new TagController(_applicationDbContextMock);

    }

    [Fact]
    public void TagController_Add_ReturnSuccess()
    {
        var response = _tagController.Add();

        response.Should().BeOfType<IActionResult>();
    }

}

错误信息

FakeItEasy.Core.FakeCreationException : 
  Failed to create fake of type blog_dotnet_mvc.DataAccess.ApplicationDbContext:

  Below is a list of reasons for failure per attempted constructor:
    Constructor with signature (Microsoft.EntityFrameworkCore.DbContextOptions`1[blog_dotnet_mvc.DataAccess.ApplicationDbContext]) failed:
      No constructor matches the passed arguments for constructor.
      An exception of type System.InvalidOperationException was caught during this call. Its message was:
      The DbContextOptions passed to the ApplicationDbContextProxy constructor must be a DbContextOptions<ApplicationDbContextProxy>. When registering multiple DbContext types, make sure that the constructor for each context type has a DbContextOptions<TContext> parameter rather than a non-generic DbContextOptions parameter.
         at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options)
         at blog_dotnet_mvc.DataAccess.ApplicationDbContext..ctor(DbContextOptions`1 options) in E:\localrepo\blog-dotnet-mvc\blog-dotnet-mvc\DataAccess\ApplicationDbContext.cs:line 8
         at Castle.Proxies.ApplicationDbContextProxy..ctor(IInterceptor[], DbContextOptions`1 options)
         at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
         at System.Reflection.ConstructorInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)


  Stack Trace: 
FailedCreationResult.get_Result() line 82
FakeAndDummyManager.CreateFake(Type typeOfFake, Action`1 optionsBuilder, LoopDetectingResolutionContext resolutionContext) line 42
FakeAndDummyManager.CreateFake(Type typeOfFake, LoopDetectingResolutionContext resolutionContext) line 28
A.Fake[T]() line 31
TagControllerTest.ctor() line 24
RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)

TagController代码

namespace blog_dotnet_mvc.Controllers
{
    public class TagController : Controller
    {

        private readonly ApplicationDbContext _dbContext;

        public TagController(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext;
        }


        [HttpGet]
        public IActionResult Add()
        {
            return View();
        }
    }
}

ApplicationDbContext代码

using blog_dotnet_mvc.Models.Domain;
using Microsoft.EntityFrameworkCore;

namespace blog_dotnet_mvc.DataAccess
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {
        }

        public DbSet<BlogPost> BlogPosts { get; set; }
        public DbSet<Tag> Tags { get; set; }
    }
}

有效写法

_applicationDbContextMock = A.Fake<ApplicationDbContext>(x => x.WithArgumentsForConstructor(() => new ApplicationDbContext(new DbContextOptions<ApplicationDbContext>())));

问题原因解释

核心报错逻辑

FakeEasy创建ApplicationDbContext的代理对象时,会自动生成一个ApplicationDbContextProxy子类(继承自你的ApplicationDbContext)。当它调用父类构造函数时,传入的是DbContextOptions<ApplicationDbContext>,但EF Core的DbContext基类会严格校验:传入的DbContextOptions泛型参数必须和当前实例的类型完全匹配——也就是需要DbContextOptions<ApplicationDbContextProxy>,而非你定义的DbContextOptions<ApplicationDbContext>,这直接触发了InvalidOperationException。

指定构造参数的解决原理

用WithArgumentsForConstructor显式指定构造参数时,FakeEasy会直接使用你提供的DbContextOptions<ApplicationDbContext>调用原始ApplicationDbContext的构造函数,跳过了自动生成代理类适配参数的逻辑。此时EF Core的校验会通过,因为构造函数接收的参数类型和当前实例的父类类型完全匹配。

更优实践

不要直接模拟DbContext,建议抽象出数据访问层的接口(比如ITagRepository),然后模拟这个接口。这样既可以避免EF Core的构造参数限制,还能让单元测试更聚焦于控制器的业务逻辑,而非EF的内部细节。

内容的提问来源于stack exchange,提问作者Kavishka Madhushan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 01:38:09