aspnetcore/test/Microsoft.AspNetCore.Mvc.Vi.../ControllerTest.cs

332 lines
12 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using Moq;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Test
{
public class ControllerTest
{
public static IEnumerable<object[]> PublicNormalMethodsFromController
{
get
{
return typeof(Controller).GetTypeInfo()
.DeclaredMethods
.Where(method => method.IsPublic &&
!method.IsSpecialName &&
!method.Name.Equals("Dispose", StringComparison.OrdinalIgnoreCase))
.Select(method => new[] { method });
}
}
[Fact]
public void SettingViewData_AlsoUpdatesViewBag()
{
// Arrange
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var controller = new TestableController();
var originalViewData = controller.ViewData = new ViewDataDictionary<object>(metadataProvider);
var replacementViewData = new ViewDataDictionary<object>(metadataProvider);
// Act
controller.ViewBag.Hello = "goodbye";
controller.ViewData = replacementViewData;
controller.ViewBag.Another = "property";
// Assert
Assert.NotSame(originalViewData, controller.ViewData);
Assert.Same(replacementViewData, controller.ViewData);
Assert.Null(controller.ViewBag.Hello);
Assert.Equal("property", controller.ViewBag.Another);
Assert.Equal("property", controller.ViewData["Another"]);
}
[Theory]
[MemberData(nameof(PublicNormalMethodsFromController))]
public void NonActionAttribute_IsOnEveryPublicNormalMethodFromController(MethodInfo method)
{
// Arrange & Act & Assert
Assert.True(method.IsDefined(typeof(NonActionAttribute)));
}
[Fact]
public void Controller_View_WithoutParameter_SetsResultNullViewNameAndNullViewDataModelAndSameTempData()
{
// Arrange
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
TempData = new TempDataDictionary(new DefaultHttpContext(), Mock.Of<ITempDataProvider>()),
};
// Act
var actualViewResult = controller.View();
// Assert
Assert.IsType<ViewResult>(actualViewResult);
Assert.Null(actualViewResult.ViewName);
Assert.Same(controller.ViewData, actualViewResult.ViewData);
Assert.Same(controller.TempData, actualViewResult.TempData);
Assert.Null(actualViewResult.ViewData.Model);
}
[Fact]
public void Controller_View_WithParameterViewName_SetsResultViewNameAndNullViewDataModelAndSameTempData()
{
// Arrange
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
TempData = new TempDataDictionary(new DefaultHttpContext(), Mock.Of<ITempDataProvider>()),
};
// Act
var actualViewResult = controller.View("CustomViewName");
// Assert
Assert.IsType<ViewResult>(actualViewResult);
Assert.Equal("CustomViewName", actualViewResult.ViewName);
Assert.Same(controller.ViewData, actualViewResult.ViewData);
Assert.Same(controller.TempData, actualViewResult.TempData);
Assert.Null(actualViewResult.ViewData.Model);
}
[Fact]
public void Controller_View_WithParameterViewModel_SetsResultNullViewNameAndViewDataModelAndSameTempData()
{
// Arrange
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
TempData = new TempDataDictionary(new DefaultHttpContext(), Mock.Of<ITempDataProvider>()),
};
var model = new object();
// Act
var actualViewResult = controller.View(model);
// Assert
Assert.IsType<ViewResult>(actualViewResult);
Assert.Null(actualViewResult.ViewName);
Assert.Same(controller.ViewData, actualViewResult.ViewData);
Assert.Same(controller.TempData, actualViewResult.TempData);
Assert.Same(model, actualViewResult.ViewData.Model);
}
[Fact]
public void Controller_View_WithParameterViewNameAndViewModel_SetsResultViewNameAndViewDataModelAndSameTempData()
{
// Arrange
var controller = new TestableController()
{
ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()),
TempData = new TempDataDictionary(new DefaultHttpContext(), Mock.Of<ITempDataProvider>()),
};
var model = new object();
// Act
var actualViewResult = controller.View("CustomViewName", model);
// Assert
Assert.IsType<ViewResult>(actualViewResult);
Assert.Equal("CustomViewName", actualViewResult.ViewName);
Assert.Same(controller.ViewData, actualViewResult.ViewData);
Assert.Same(controller.TempData, actualViewResult.TempData);
Assert.Same(model, actualViewResult.ViewData.Model);
}
[Fact]
public void Controller_Json_WithParameterValue_SetsResultData()
{
// Arrange
var controller = new TestableController();
var data = new object();
// Act
var actualJsonResult = controller.Json(data);
// Assert
Assert.IsType<JsonResult>(actualJsonResult);
Assert.Same(data, actualJsonResult.Value);
}
[Fact]
public void Controller_Json_WithParameterValue_SetsRespectiveProperty()
{
// Arrange
var controller = new TestableController();
var data = new object();
var serializerSettings = new JsonSerializerSettings();
// Act
var actualJsonResult = controller.Json(data, serializerSettings);
// Assert
Assert.IsType<JsonResult>(actualJsonResult);
Assert.Same(data, actualJsonResult.Value);
}
// These tests share code with the ActionFilterAttribute tests because the various filter
// implementations need to behave the same way.
[Fact]
public async Task Controller_ActionFilter_SettingResult_ShortCircuits()
{
// Arrange, Act & Assert
await CommonFilterTest.ActionFilter_SettingResult_ShortCircuits(
new Mock<Controller>());
}
[Fact]
public async Task Controller_ActionFilter_Calls_OnActionExecuted()
{
// Arrange, Act & Assert
await CommonFilterTest.ActionFilter_Calls_OnActionExecuted(
new Mock<Controller>());
}
[Fact]
public void ControllerDispose_CallsDispose()
{
// Arrange
var controller = new DisposableController();
// Act
controller.Dispose();
// Assert
Assert.True(controller.DisposeCalled);
}
[Fact]
public void TempData_CanSetAndGetValues()
{
// Arrange
var controller = GetController(null, null);
var input = "Foo";
// Act
controller.TempData["key"] = input;
var result = controller.TempData["key"];
// Assert
Assert.Equal(input, result);
}
public static TheoryData<object, Type> IncompatibleModelData
{
get
{
// Small grab bag of instances and expected types with no common base except typeof(object).
return new TheoryData<object, Type>
{
{ null, typeof(object) },
{ true, typeof(bool) },
{ 43.78, typeof(double) },
{ "test string", typeof(string) },
{ new List<int>(), typeof(List<int>) },
{ new List<string>(), typeof(List<string>) },
};
}
}
[Theory]
[MemberData(nameof(IncompatibleModelData))]
public void ViewDataModelSetter_DoesNotThrow(object model, Type expectedType)
{
// Arrange
var activator = new ViewDataDictionaryControllerPropertyActivator(new EmptyModelMetadataProvider());
var actionContext = new ActionContext(
new DefaultHttpContext(),
new RouteData(),
new ControllerActionDescriptor());
var controllerContext = new ControllerContext(actionContext);
var controller = new TestableController();
activator.Activate(controllerContext, controller);
// Guard
Assert.NotNull(controller.ViewData);
// Act (does not throw)
controller.ViewData.Model = model;
// Assert
Assert.NotNull(controller.ViewData.ModelMetadata);
Assert.Equal(expectedType, controller.ViewData.ModelMetadata.ModelType);
}
private static Controller GetController(IModelBinder binder, IValueProvider valueProvider)
{
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var httpContext = new DefaultHttpContext();
var viewData = new ViewDataDictionary(metadataProvider, new ModelStateDictionary());
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
var valiatorProviders = new[]
{
new DataAnnotationsModelValidatorProvider(
new ValidationAttributeAdapterProvider(),
new TestOptionsManager<MvcDataAnnotationsLocalizationOptions>(),
stringLocalizerFactory: null),
};
valueProvider = valueProvider ?? new SimpleValueProvider();
var controllerContext = new ControllerContext()
{
HttpContext = httpContext,
ValueProviderFactories = new[] { new SimpleValueProviderFactory(valueProvider), },
};
var controller = new TestableController()
{
ControllerContext = controllerContext,
MetadataProvider = metadataProvider,
ObjectValidator = new DefaultObjectValidator(metadataProvider, valiatorProviders),
TempData = tempData,
ViewData = viewData,
};
return controller;
}
private class DisposableController : Controller
{
public bool DisposeCalled { get; private set; }
protected override void Dispose(bool disposing)
{
DisposeCalled = true;
}
}
private class TestableController : Controller
{
}
private class DisposableObject : IDisposable
{
public void Dispose()
{
throw new NotImplementedException();
}
}
}
}