Нэнси, тестирующая GetModel‹T›, выдает KeyNotFoundException

Я пытаюсь проверить, что модель, возвращенная из моего приложения Нэнси, соответствует ожиданиям. Я следил за документацией здесь но всякий раз, когда я вызываю метод расширения GetModel<T>, он выдает KeyNotFoundException.

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.

Я знаю, что означает ошибка, но я не понимаю, почему она возникает.

Вот мой модуль

public class SanityModule : NancyModule
{
    public SanityModule()
    {
        Get["sanity-check"] = _ => Negotiate.WithModel(new SanityViewModel { Id = 1 })
                                            .WithStatusCode(HttpStatusCode.OK);
    }
}

моя модель просмотра

public class SanityViewModel
{
    public int Id { get; set; }
}

и вот мой тест

[TestFixture]
public class SanityModuleTests
{
    [Test]
    public void Sanity_Check()
    {
        // Arrange
        var browser = new Browser(with =>
        {
            with.Module<SanityModule>();
            with.ViewFactory<TestingViewFactory>();
        });

        // Act
        var result = browser.Get("/sanity-check", with =>
        {
            with.HttpRequest();
            with.Header("accept", "application/json");
        });
        var model = result.GetModel<SanityViewModel>();

        // Asset
        model.Id.ShouldBeEquivalentTo(1);
    }
}

Отладка этого теста показывает, что модуль срабатывает и завершается нормально. Запуск приложения показывает, что ответ соответствует ожидаемому.

Может кто-нибудь пролить некоторый свет на это?


person Macs Dickinson    schedule 03.09.2014    source источник
comment
Я считаю, что GetModel<T> предназначен для получения модели за визуализированным html-представлением. Вы можете просто десериализовать json в SanityModel, чтобы проверить его.   -  person albertjan    schedule 04.09.2014


Ответы (1)


Спасибо прекрасным парням, albertjan и the.fringe.ninja, в Комната Нэнси Джаббр у нас есть объяснение, что здесь происходит.

TL;DR Логично, что это не работает, но сообщение об ошибке должно быть более информативным. Ниже приведен обходной путь.

Проблема здесь в том, что я запрашиваю ответ как application/json при использовании TestingViewFactory.

Давайте посмотрим на реализацию GetModel<T>();

public static TType GetModel<TType>(this BrowserResponse response)
{
    return (TType)response.Context.Items[TestingViewContextKeys.VIEWMODEL];
}

Это просто захват модели представления из NancyContext и приведение ее к вашему типу. Здесь выдается ошибка, так как в NancyContext нет модели представления. Это связано с тем, что модель представления добавляется в NancyContext в методе RenderView из TestingViewFactory.

public Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
{
    // Intercept and store interesting stuff
    viewLocationContext.Context.Items[TestingViewContextKeys.VIEWMODEL] = model;
    viewLocationContext.Context.Items[TestingViewContextKeys.VIEWNAME] = viewName;
    viewLocationContext.Context.Items[TestingViewContextKeys.MODULENAME] = viewLocationContext.ModuleName;
    viewLocationContext.Context.Items[TestingViewContextKeys.MODULEPATH] = viewLocationContext.ModulePath;

    return this.decoratedViewFactory.RenderView(viewName, model, viewLocationContext);
}

Мой тест запрашивает json, поэтому RenderView не будет вызываться. Это означает, что вы можете использовать GetModel<T> только при использовании html-запроса.

Временное решение

Мое приложение представляет собой API, поэтому у меня нет представлений, поэтому меняю строку

with.Header("accept", "application/json");

to

with.Header("accept", "text/html");

выкинет ViewNotFoundException. Чтобы избежать этого, мне нужно реализовать свой собственный IViewFactory. (это взято из the.fringe.ninja)

public class TestViewFactory : IViewFactory
{
    #region IViewFactory Members

    public Nancy.Response RenderView(string viewName, dynamic model, ViewLocationContext viewLocationContext)
    {
        viewLocationContext.Context.Items[Fixtures.SystemUnderTest.ViewModelKey] = model;
        return new HtmlResponse();
    }

    #endregion
}

Тогда это просто случай обновления

with.ViewFactory<TestingViewFactory>();

to

with.ViewFactory<TestViewFactory>();

Теперь GetModel<T> должен работать без представления.

person Macs Dickinson    schedule 04.09.2014
comment
Ой, моя голова... В итоге я просто сам десериализовал ответ, вместо того, чтобы пытаться заставить GetModel<> работать. - person ESG; 20.06.2016
comment
Хорошее объяснение — по крайней мере, я знаю, что я не сумасшедший. К сожалению, это не сработало для меня. Я использую MSTest, поэтому Fixtures.SystemUnderTest.ViewModelKey для меня не определено. - person JamesQMurphy; 18.12.2016