0

语境

.NET 5 控制台应用程序使用 IHostedService 模式和 EntityFramework Core 5。

问题

dbContext 如下所示:

    public class WeatherDbContext : DbContext, IWeatherDbContext
    {
        public WeatherDbContext(DbContextOptions<WeatherDbContext> options) : base(options)
        {
        }

       public virtual DbSet<Observation> Observations { get; set; }
}

主机构建器是这样配置的:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
        })
        .UseSerilog((hostContext, loggerConfiguration) =>
        {
            logConfiguration.WriteTo.File(ConfigurationManager.AppSettings["LogFile"]);
        })
        .ConfigureServices((services) =>
        {
            services.AddHttpClient()
                    .AddSingleton<CommandLineArguments>(new CommandLineArguments(args))
                    .AddSingleton<StringWriter>()
                    .AddDbContext<IWeatherDbContext, WeatherDbContext>(options =>
                    {
                        options.UseSqlServer(ConfigurationManager.ConnectionStrings["WeatherManagerDatabase"].ConnectionString);
                    })   
                    .AddTransient<IWeatherUndergroundAPIService(x => new WeatherUndergroundAPIService(ConfigurationManager.AppSettings["StationId"],
                                                                                                      ConfigurationManager.AppSettings["WUApiKey"],
                                                                                                      x.GetRequiredService<IHttpClientFactory>()))
                   .AddHostedService<DataDownloader>();                                                                                        
        });

...并且主机服务是这样构造的:

private readonly int importDayLimit;
private readonly ILogger logger;
private readonly StringWriter outputWriter;
private readonly int throttleLimit = 100;
private readonly IWeatherDbContext weatherDbContext;
private readonly IWeatherUndergroundAPIService wuApiService;
private DateTime FetchUpToDate;
private DateTime MostRecentlyRecordedObservationDate;

public DataDownloader(IWeatherUndergroundAPIService wuApiService,
                      ILogger logger,
                      IWeatherDbContext weatherDbContext,
                      StringWriter outputWriter,
                      CommandLineArguments commandLineArguments)
{
    this.wuApiService = wuApiService;
    this.weatherDbContext = weatherDbContext;
    this.logger = logger;
    this.outputWriter = outputWriter;
    this.importDayLimit = this.ProcessCommandLineArguments(commandLineArguments.Args);
}

然后我有一个这样的 XUnit 测试:

public class CommandLineArgumentValidation
{
    [Fact]
    public async Task CommandLineArgumentNotAnIntegerAsync()
    {
        // Arrange

        Mock<IWeatherUndergroundAPIService> mockWeatherUndergroundAPIService = new();

        DbContextOptions<WeatherDbContext> dbContextOptions = new DbContextOptionsBuilder<WeatherDbContext>()
            .UseInMemoryDatabase(databaseName: "testDb")
            .EnableDetailedErrors()
            .Options;

        IWeatherDbContext weatherDbContext = new WeatherDbContext(dbContextOptions);

        Mock<ILogger> mockLogger = new();
        
        StringBuilder consoleOutput = new();
        StringWriter consoleWriter = new(consoleOutput);
        
        CommandLineArguments commandLineArguments = new(new string[] { "not a positive integer" });

        DataDownloader dataDownloader = new(mockWeatherUndergroundAPIService.Object,
                                           mockLogger.Object,
                                           weatherDbContext,
                                           consoleWriter,
                                           commandLineArguments);
        
        CancellationToken cancellationToken = new(false);

        // Action

        await dataDownloader.StartAsync(cancellationToken);

        // Assertion

        Assert.Equal("Command line argument 'not a positive integer' is not a positive integer. Aborting download.", consoleOutput.ToString());
    }
}

测试抛出异常

Castle.DynamicProxy.InvalidProxyConstructorArgumentsException:无法实例化类的代理:WeatherManagerDataDownloader.WeatherDbContext。找不到与给定参数匹配的构造函数:

请注意,为了清楚起见,我已经简化了代码。我模拟其他注入 DataDownloader 的服务,但我不是在模拟 dBContext。编辑:我现在已经添加了完整的代码,因为有人指出,即使我的简化代码没有暗示,模拟似乎正在发生。

问题

为什么会发生此测试异常?正如我所看到的,模拟应该与传入的 dBContext 无关。

4

1 回答 1

0

认为这可能是答案,当然它允许我继续前进。

使用调试器运行测试时,我意识到一切都已成功注入,并且测试的代码正在成功进行。但是我正在测试无效的命令行参数情况,并且在 DataDownloader 中我已经编码

Environment.Exit(1)

在输出适当的消息后停止应用程序。显然,这不是您应该在 IHostedService 中执行的操作,删除它会使模拟错误消失。然而,为什么它会导致尚未被模拟的东西的模拟错误仍然令人费解。

于 2021-05-13T16:03:11.467 回答