Как установить ссылку на объект для экземпляра объекта - PullRequest
0 голосов
/ 03 мая 2018

Я пишу тест Xunit для приведенного ниже контроллера. когда я запускаю тест, я получаю эту ошибку

object reference is notset to an instance of an object

Это мой тестовый код

  [Fact]
    public async Task RegistrationTest()
    {
 ctrl = new AccountsControllers(context, userManager, jwtFactory, jwtoptions);
       ctrl.ModelState.AddModelError("x", "Model error");

        var mod = new RegistrationViewModel
        {
            Email = "johnmiller@sins.com"
        };
        IActionResult result = await ctrl.Register(mod);
       // Assert.Equal(mod.Email, moe);
        var viewresult = Assert.IsType<BadRequestObjectResult>(result);

    }

Однако я успешно создаю экземпляры для контекста, usermanager

var services = new ServiceCollection();

        services.AddDbContext<ApplicationContext>(options => options
                .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Inventory;Trusted_Connection=True;ConnectRetryCount=0"));

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationContext>();

        var con = new DefaultHttpContext();

        con.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());

        services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = con });

var serviceProvider = services.BuildServiceProvider();

        context = serviceProvider.GetRequiredService<ApplicationContext>();
        userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();

Я не понимаю, как создать для IJwtFactory и JwtIssuerOptions

Это мой конструктор контроллеров аккаунтов

 public AccountsControllers(ApplicationContext context, UserManager<ApplicationUser> userManager, 
            IJwtFactory jwtFactory, IOptions<JwtIssuerOptions> jwtoptions)
    {
        appDbContext = context;

        this.userManager = userManager;
        this.jwtFactory = jwtFactory;
        jwtOptions = jwtoptions.Value;

        serializerSettings = new JsonSerializerSettings
        {
            Formatting = Formatting.Indented
        };
    }

Это метод Login внутри контроллера учетных записей.

 public async Task<IActionResult> Login([FromBody]LoginViewModel credentials)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }


        var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);

        if (identity == null)
        {
            return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
        }
        var response = new
        {
            id = identity.Claims.Single(c => c.Type == "id").Value,
           auth_token = await jwtFactory.GenerateEncodedToken(credentials.UserName, identity),
            expires_in = (int)jwtOptions.ValidFor.TotalSeconds
        };

        var json = JsonConvert.SerializeObject(response, serializerSettings);
        return new OkObjectResult(json);
    }

ClaimsIdentity

    public async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password)
    {

        if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
        {
            // get the user to verifty
            var userToVerify = await userManager.FindByNameAsync(userName);


            if (userToVerify != null)
            {

                // check the credentials  
              if (await userManager.CheckPasswordAsync(userToVerify, password))
               {
                   return await Task.FromResult(jwtFactory.GenerateClaimsIdentity(userName, userToVerify.Id));
                }

            }

        }

        return await Task.FromResult<ClaimsIdentity>(null);
    }

1 Ответ

0 голосов
/ 03 мая 2018

Проблема в том, что вы пытаетесь смоделировать все свои многочисленные зависимости, и вы явно что-то упускаете. Всякий раз, когда вы обнаруживаете, что вам нужно смоделировать более двух зависимостей, это довольно явный признак того, что вы находитесь на территории интеграционного тестирования.

Как правило, каждый раз, когда вы тестируете действие контроллера, вы тестируете интеграцию. Хотя они выглядят как методы в классе, в рамках конвейера запросов должно произойти так много, что они действительно должны функционировать правильно, но вы не сможете реально протестировать их без реального запроса. В частности, такие вещи, как создание HttpContext с аутентифицированным пользователем, не происходят вне конвейера запросов. Вы также можете технически насмехаться над всем этим, но опять же, это просто больше зависимостей, которые вы должны предоставить вручную, и больше возможностей для создания тестов, которые не пройдены, потому что настройка не правильная, а не потому, что что-то на самом деле неверно .

Так как это действительно так, на самом деле выполните настоящий интеграционный тест и используйте TestServer. Вы можете указать ему свой класс Startup, чтобы все сервисы регистрировались и вводились так, как они должны, затем вы просто делаете запрос и проверяете объект ответа. Для насмешливых действий, таких как подключение к вашей базе данных, вы должны настроить «тестовую» среду и настроить ее в файле Startup.cs для использования таких вещей, как поставщик базы данных EF в памяти. Затем вы можете указать, что ваш TestServer должен использовать эту "тестовую" среду, и все готово. См. документацию для получения дополнительной информации.

...