微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ASP.NET Core TestHost 集成测试“System.ArgumentNullException:未将字符串引用设置为字符串的实例”

如何解决ASP.NET Core TestHost 集成测试“System.ArgumentNullException:未将字符串引用设置为字符串的实例”

我正在使用 xunit 和

实现集成测试
<packagereference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />

按照本教程: https://dotnetcorecentral.com/blog/asp-net-core-web-api-integration-testing-with-xunit/

当我运行测试时,我收到此消息

未将字符串引用设置为字符串的实例

IntegrationTests.Categories.TestAdd:
结果:失败
错误信息:
System.ArgumentNullException:未将字符串引用设置为字符串的实例。

参数名称:s

堆栈跟踪:

在 System.Text.Encoding.GetBytes(String s)
在/Users/josueaceves/Desktop/dopshop/Startup.cs:line 114
中的Dopshop.Startup.ConfigureServices(IServiceCollection services) --- 从上一个抛出异常的位置开始的堆栈跟踪结束---
在 Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection 服务)
在 Microsoft.AspNetCore.Hosting.Internal.WebHost.Ensureapplicationservices()
在 Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
在 Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
在 Microsoft.AspNetCore.TestHost.TestServer..ctor(IWebHostBuilder builder,IFeatureCollection featureCollection)
在/Users/josueaceves/Desktop/dopshop/IntegrationTests/TestClientProvider.cs:line 17
中的IntegrationTests.TestClientProvider..ctor() 在/Users/josueaceves/Desktop/dopshop/IntegrationTests/Categories.cs:line 31
中的IntegrationTests.Categories.TestAdd() --- 从上一个抛出异常的位置开始的堆栈跟踪结束---

IntegrationTests.Categories.TestGetAll:
结果:失败
错误信息:
System.ArgumentNullException:未将字符串引用设置为字符串的实例。

参数名称:s

堆栈跟踪:

在 System.Text.Encoding.GetBytes(String s)
在/Users/josueaceves/Desktop/dopshop/Startup.cs:line 114
中的Dopshop.Startup.ConfigureServices(IServiceCollection services) --- 从上一个抛出异常的位置开始的堆栈跟踪结束---
在 Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection 服务)
在 Microsoft.AspNetCore.Hosting.Internal.WebHost.Ensureapplicationservices()
在 Microsoft.AspNetCore.Hosting.Internal.WebHost.Initialize()
在 Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
在 Microsoft.AspNetCore.TestHost.TestServer..ctor(IWebHostBuilder builder,IFeatureCollection featureCollection)
在/Users/josueaceves/Desktop/dopshop/IntegrationTests/TestClientProvider.cs:line 17
中的IntegrationTests.TestClientProvider..ctor() 在/Users/josueaceves/Desktop/dopshop/IntegrationTests/Categories.cs:line 19
中的IntegrationTests.Categories.TestGetAll() --- 从上一个抛出异常的位置开始的堆栈跟踪结束---

总测试数:2。通过:0。失败:2。跳过:0

这是在启动文件第 114 行:

TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
                    .GetBytes(this.Configuration.GetSection("AppSettings:Token").Value)),ValidateIssuer = false,ValidateAudience = false
            };

我有我的ClientTestProvider

using System;
using System.Net.Http;
using Dopshop;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;

namespace IntegrationTests
{
    public class TestClientProvider
    {
        private TestServer server;
        public HttpClient Client { get; private set; }

        public TestClientProvider()
        {

            server = new TestServer(new WebHostBuilder().UseStartup<Startup>());

            Client =  server.CreateClient();
        }
    }
}

还有我的两个集成测试

using System.Net;
using System.Threading.Tasks;
using Contracts.v1;
using Xunit;
using FluentAssertions;
using System.Net.Http;
using Newtonsoft.Json;
using System.Text;
using Dopshop.Models;

namespace IntegrationTests
{
    public class Categories
    {
        [Fact]
        public async Task TestGetAll()
        {
            using (var client = new TestClientProvider().Client)
            {
                var response = await client.GetAsync(ApiRoutes.Categories.GetAll);
                response.EnsureSuccessstatusCode();

                response.StatusCode.Should().Be(HttpStatusCode.OK);
            }
        }

        [Fact]
        public async Task TestAdd()
        {
            using (var client = new TestClientProvider().Client)
            {
                var response = await client.PostAsync(ApiRoutes.Categories.Add,new StringContent(
                        JsonConvert.SerializeObject(new Category(){Name = "nike Shoes",Description = "shoe category",Sequence = 2,ShopId = 3}),Encoding.UTF8,"application/json"));

                response.EnsureSuccessstatusCode();

                response.StatusCode.Should().Be(HttpStatusCode.OK);
            }
        }
    }
} 

有人知道这是怎么回事吗?我是在 .NET Core 中测试的新手。如果有人能指出我正确的方向,我将不胜感激。

<packagereference Include="FluentValidation.AspNetCore" Version="9.1.1"/>
<packagereference Include="Microsoft.AspNetCore.App"/>
<packagereference Include="Pomelo.EntityFrameworkCore.MysqL" Version="2.1.1"/>
<packagereference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0"/>
<packagereference Include="Swashbuckle.AspNetCore" Version="5.4.0"/>
<packagereference Include="CloudinaryDotNet" Version="1.11.0"/>
<packagereference Include="xunit" Version="2.4.0"/>
<packagereference Include="xunit.runner.visualstudio" Version="2.4.0"/>
<packagereference Include="Moq" Version="4.14.5"/>
<packagereference Include="Microsoft.NET.Test.Sdk" Version="16.7.1"/>
<packagereference Include="SendGrid" Version="9.21.0"/>
<packagereference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
<packagereference Include="FluentAssertions" Version="5.10.3" /> 

这是启动文件

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Authorization;
using AutoMapper;
using Data;
using Filters;
using FluentValidation.AspNetCore;
using Dopshop.Models;
using Dopshop.Services.Email;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Repositories.Implementations;
using Repositories.Interfaces;
using Services.Email;
using Services.Photos;
using Settings;
using Microsoft.AspNetCore.Http;
using static Contracts.v1.Validation.Validators;
using Newtonsoft.Json.Linq;
using System.Net;
using Services.Authentication;

namespace Dopshop
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DopshopContext>(x => x.UseMysqL(Configuration.GetConnectionString("DefaultConnection")));
            
            services.AddMvc(options => {
                options.Filters.Add<ValidationFilter>();
            })
                .AddJsonoptions(options => {
                    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                })
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<CreateShopValidator>());;
            
            services.Configure<ApiBehaviorOptions>(options =>
            {
                options.SuppressModelStateInvalidFilter = true;
            });
            
            services.AddCors(options => {
                options.AddPolicy(name: "ProductionPolicy",builder => {
                        builder.WithOrigins("https://hustlr.azurewebsites.net","https://hustlr.cards","https://hustlr-39c77.web.app","https://hustlr-39c77.firebaseapp.com")
                                            .WithMethods("*")
                                            .WithHeaders("*");
                    });
            });

            services.AddSwaggerGen(option => {

                option.SwaggerDoc("v1",new OpenApiInfo{ Title  = "Dopshop API",Version = "v1"});

                option.AddSecurityDeFinition("Bearer",new OpenApiSecurityScheme
                {
                    Description = "JWT Authorization header using the bearer scheme",Name = "Authorization",In = Microsoft.OpenApi.Models.ParameterLocation.Header,Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey,Scheme = "Bearer"
                });
                
                option.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,Id = "Bearer"
                            },Scheme = "oauth2",Name = "Bearer",In = ParameterLocation.Header,},new List<string>()
                    }
                });
                
                // Set the comments path for the Swagger JSON and UI.
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory,xmlFile);
                option.IncludeXmlComments(xmlPath);
                });

            TokenValidationParameters validationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,ValidateAudience = false
            };

            services.AddAuthentication(options => 
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options => {
                    options.Savetoken = true;
                    options.TokenValidationParameters = validationParameters;
                    options.Events = new JwtBearerEvents();
                    options.Events.OnChallenge = context =>
                    {
                        // Skip the default logic.
                        context.HandleResponse();

                        var payload = new JObject
                        {
                            ["code"] = 401,["message"] = "Invalid JWT Token"
                        };

                        context.Response.ContentType = "application/json";
                        context.Response.StatusCode = 401;

                        return context.Response.WriteAsync(payload.ToString());
                    };
            });

            services.AddSingleton<TokenValidationParameters>(validationParameters);

            services.Configure<CloudinarySettings>(Configuration.GetSection("CloudinarySettings"));
            services.Configure<SendGridAPISettings>(Configuration.GetSection("SendGridSettings"));
            
            services.AddAutoMapper(typeof(Startup).Assembly);

            services.AddTransient<IShopRepository,ShopRepository>();
            services.AddTransient<IAuthRepository,AuthRepository>();
            services.AddTransient<IUserRepository,UserRepository>();
            services.AddTransient<IProductRepository,ProductRepository>();
            services.AddTransient<ICategoryRepository,CategoryRepository>();
            services.AddTransient<IProductCategoryRepository,ProductCategoryRepository>();
            services.AddTransient<IPhotoRepository,PhotoRepository>();
            services.AddTransient<IProductPhotosRepository,ProductPhotosRepository>();
            services.AddTransient<IReviewsRepository,ReviewsRepository>();
            services.AddTransient<ICategoryRepository,CategoryRepository>();
            services.AddTransient<IGenericRepository<ReviewPhoto>,GenericRepository<ReviewPhoto>>();
            services.AddTransient<IGenericRepository<ShopTheme>,GenericRepository<ShopTheme>>();
            services.AddTransient<IGenericRepository<Currency>,GenericRepository<Currency>>();
            services.AddTransient<IGenericRepository<Industry>,GenericRepository<Industry>>();
            services.AddTransient<IGenericRepository<ShopLocation>,GenericRepository<ShopLocation>>();
            services.AddTransient<IGenericRepository<ShopPaymentMethod>,GenericRepository<ShopPaymentMethod>>();
            services.AddTransient<IGenericRepository<PaymentMethodType>,GenericRepository<PaymentMethodType>>();
            services.AddTransient<IGenericRepository<SocialLink>,GenericRepository<SocialLink>>();
            services.AddTransient<IGenericRepository<LinkType>,GenericRepository<LinkType>>();
            services.AddTransient<IGenericRepository<Category>,GenericRepository<Category>>();
            services.AddTransient<IGenericRepository<ProductCategory>,GenericRepository<ProductCategory>>();
            services.AddTransient<IProductOptionRepository,ProductOptionRepository>();
            services.AddTransient<IProductOptionValueRepository,ProductOptionValueRepository>();
            services.AddTransient<IProductQuestionRepository,ProductQuestionRepository>();
            services.AddTransient<IProductvariationRepository,ProductvariationRepository>();
            services.AddTransient<IProductOptionValueVariationRepository,ProductOptionValueVariationRepository>();
            services.AddTransient<IRefreshTokenRepository,RefreshTokenRepository>();
            services.AddTransient<ISocialLinkRepository,ShopSocialLinkRepository>();
            services.AddTransient<IShopPaymentMethodsRepository,ShopPaymentMethodsRepository>();
            services.AddTransient<IReviewSentimentRepository,ReviewSentimentRepository>();
            services.AddTransient<IPasswordRecoveryRepository,PasswordRecoveryRepository>();
            services.AddTransient<IEmailService,SendGridEmailSender>();
            services.AddTransient<IPhotosService,CloudinaryPhotosService>();
            services.AddTransient<IAuthorizationHandlers,AuthorizationHandlers>();
            services.AddTransient<IAuthTokenGenerator,AuthTokenGenerator>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app,IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
                app.UseSwagger();
                app.UseSwaggerUI(c => { 
                    c.SwaggerEndpoint("/swagger/v1/swagger.json","Hustlr API V1");
                    c.RoutePrefix = string.Empty;
                });

                app.UseDefaultFiles();
                app.UseStaticFiles();
            }
            else
            {
                app.UseCors("ProductionPolicy");
                app.UseHsts();
                //app.UseHttpsRedirection();
            }

            app.UseStatusCodePages(context => {
                var request = context.HttpContext.Request;
                var response = context.HttpContext.Response;
                response.ContentType = "application/json";
                if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
                {
                    var payload = new JObject
                    {
                        ["code"] = 401,["message"] = "Unauthorized"
                    };
                    response.WriteAsync(payload.ToString());
                }

                return Task.CompletedTask;
            });
            
            app.UseAuthentication();
            app.UseMvc();
        }
    }
}

这是我的 Program.cs 文件

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Data;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Dopshop
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateWebHostBuilder(args).Build();

            using (var scope = host.Services.CreateScope())
            {
                var db = scope.ServiceProvider.GetService<DopshopContext>();
                db.Database.Migrate();
            }

            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseUrls("http://*:5000");
    }
}

解决方法

我发现我的问题出在 WebHost 的配置上。我只是在 Program.cs 文件中模仿了我已经在做的事情。

    public TestClientProvider()
            {
                
                server = new TestServer(WebHost.CreateDefaultBuilder().UseStartup<Startup>());
                Client =  server.CreateClient();
            }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。