I have built unit testing for my service layer. I have not used Mock as I think that since you are adding/deleting/querying a database, why query a mock as the results could be different, but that isn't what I am asking.
Now I am using Moq to test my web api layer. I think that this is fine, as if all my tests pass on the service layer, it is fine to mock the services to test the web api.
I have managed to write a test for my GetAsync method and it works all fine, like so
Here is the controller:
public async Task<IHttpActionResult> GetAsync(long id)
{
Content content = await _service.GetAsync(id);
ContentModel model = Mapper.Map<ContentModel>(content);
return Ok(model);
}
Here is the test:
[TestMethod]
public void Content_GetAsync()
{
// arrange
var mockService = new Mock<IContentService>();
mockService.Setup(x => x.GetAsync(4))
.ReturnsAsync(new Content
{
Id = 4
});
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.GetAsync(4).Result;
var contentResult = actionResult as OkNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual(4, contentResult.Content.Id);
}
I believe I wrote this correctly, and it seems to work. Now I would like to test my PostAsync method to add an item. The controller looks like this:
public async Task<IHttpActionResult> PostAsync(ContentModel model)
{
Content content = Mapper.Map<Content>(model);
await _service.AddAsync(content);
return Created<ContentModel>(Request.RequestUri, Mapper.Map<ContentModel>(content));
}
And here is the test:
[TestMethod]
public void Content_PostAsync()
{
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}
Now when I run this, I get an error:
null reference exception. "Request" from the Request.RequestUri is null.
So I changed my controller and tests to this, to try and mock it.
Test code:
public Task<IHttpActionResult> PostAsync(ContentModel model)
{
return PostAsync(model, Request);
}
/// Unit testable version of above. Cannot be accessed by users
[NonAction]
public async Task<IHttpActionResult> PostAsync(ContentModel model, System.Net.Http.HttpRequestMessage request)
{
Content content = Mapper.Map<Content>(model);
await _service.AddAsync(content);
return Created<ContentModel>(request.RequestUri, Mapper.Map<ContentModel>(content));
}
Controller code:
[TestMethod]
public void Content_PostAsync()
{
// arrange
var mockRequest = new Mock<System.Net.Http.HttpRequestMessage>();
mockRequest.Setup(e => e.RequestUri)
.Returns(new Uri("http://localhost/"));
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, mockRequest.Object).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}
Now I get an error saying:
Invalid setup on a non-virtual (overridable in VB) member: e => e.RequestUri
Can someone please, please help me with this. I am sure I am using Mock correctly in all tests, but unit testing is new to me, so maybe I am just not doing something right.
With Moq you can only mock virtual/absrtact members. The RequestUri is not a virtual member of HttpRequestMessage, hence the error message.
You should be able to just new a HttpRequestMessage directly without mocking it and pass that in.
var request = System.Net.Http.HttpRequestMessage>();
request.RequestUri = new Uri("http://localhost/");
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, request).Result;
Ned's answer is correct. Moq is a constrained mocking library, meaning that it generates dynamic subclasses of the classes you mock at runtime. These subclasses cannot override methods if they are not declared virtual in the mocked class. You can find more information on constrained vs. unconstrained mocking libraries in the art of unit testing.
That's why people that use a mockist style of unit testing prefer to mock against interfaces instead of concrete classes, as the generated mock subclasses can easily override (or rather, implement) the methods on the interface.
Related
So I am very new to writing tests. I created an ASP.NET core web api along with angular. I have to write unit tests for the web API controllers. I have been reading Microsoft documentation on how to get started with unit tests of ASP.NET web APIs. But I am still very unsure on how to go about writing proper tests.
My Controller Code
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class OrdersController : ControllerBase
{
private readonly IOrderRepository _repo;
private readonly IMapper _mapper;
public OrdersController(IOrderRepository repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
[AllowAnonymous]
[HttpPost()]
public async Task<IActionResult> AddOrder(OrderForMappingDto orderForMappingDto)
{
//if(orderForMappingDto.ARentalOrNot == null)
//{
// throw new Exception("Value can't be left null");
//}
var orderToCreate = _mapper.Map<TblOrder>(orderForMappingDto);
var createdOrder = await _repo.AddOrder(orderToCreate);
return Ok(createdOrder);
}
}
My Repository Code
public class OrderRepository : IOrderRepository
{
private readonly MovieRentalDBContext _context;
public OrderRepository(MovieRentalDBContext context)
{
_context = context;
}
public async Task<TblOrder> AddOrder(TblOrder tblOrder)
{
await _context.TblOrder.AddAsync(tblOrder);
await _context.SaveChangesAsync();
return tblOrder;
}
}
I understand there is a lot of mocking to be done. But do I need to mock the Entity Framework as well?
I wrote a simple test file.
public void PostsAorder_WhenCalled_ReturnsOkWithResponse()
{
var mockOrderRepository = new Mock<IOrderRepository>();
var mockOrderMapper = new Mock<IMapper>();
var orderControllerObject = new OrdersController(mockOrderRepository.Object, mockOrderMapper.Object);
Task<IActionResult> contentResult = orderControllerObject.AddOrder(new OrderForMappingDto
{
ACustomerId = 3,
AMovieId = 18,
ARentalOrNot = false,
AOrderedDate = DateTime.Now
}) ;
//var contentResult = actionResult as OkNegotiatedContentResult<OrderForMappingDto>;
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Result);
}
The OkNegotioatedContent function doesn't work with Tasks. How do I go about using that for task. Also, the tests is passing even when I don't supply the last 3 arguments even though in the DTO they are classified as [Required]. Can somebody help on how to modify the test properly.
mapper Configuration-
Your test is almost okay. First the fixed version then some explanation:
[Fact]
public async Task GivenAValidOrder_WhenICallTheAsOrder_ThenItReturnsOkWithResponse()
{
//Arrange
var mockOrderMapper = new Mock<IMapper>();
mockOrderMapper.Setup(mapper => mapper.Map<TblOrder>(It.IsAny<OrderForMappingDto>()))
.Returns(new TblOrder());
var mockOrderRepository = new Mock<IOrderRepository>();
mockOrderRepository.Setup(repo => repo.AddOrder(It.IsAny<TblOrder>()))
.ReturnsAsync((TblOrder order) => order);
var SUT = new OrdersController(mockOrderRepository.Object, mockOrderMapper.Object);
//Act
var contentResult = await SUT.AddOrder(new OrderForMappingDto
{
ACustomerId = 3,
AMovieId = 18,
ARentalOrNot = false,
AOrderedDate = DateTime.Now
});
//Assert
Assert.NotNull(contentResult);
Assert.IsAssignableFrom<OkObjectResult>(contentResult);
var result = ((OkObjectResult)contentResult).Value;
Assert.NotNull(result);
Assert.IsAssignableFrom<TblOrder>(result);
}
The name of test follows the Given When Then structure to make it easier to understand that under what circumstances how should the controller's action behave.
The test is now asynchronous because we need to await the controller's action to finish in order to examine its result.
I've added the Arrange Act Assert comments to the code in order to emphasize which phase starts when.
I've set the mapper mock to return a new TblOrder and the repo mock to return whatever it receives.
I've renamed orderControllerObject to SUT, because it emphasize which component is under examination (System Under Test).
In the result verification I've used IsAssingableForm instead of IsType because it checks against derived classes as well. In this particular case it is not mandatory, but it is a good practice.
The result type will be OkObjectResult not OkNegotiatedContentResult, so you should check against that.
And finally I've added an extra check to make sure that the returned object's type is that what is expected.
I am Making Mock of Register Agency User But It showing some of its attribute Null.
I am new and I don't understand this.
I am trying to make Mock of Register Agency User that is the model
[TestMethod]
public async Task ABCreateActionResult_ReturnsBadRequest_Badrequest()
{
RegisterAgencyUserRequest mock = new Mock<RegisterAgencyUserRequest>().Object;
var controller = _accountController.RegisterAgencyUser(mock);
JsonResult viewResult = (JsonResult)await _accountController.RegisterAgencyUser(mock);
}
Here is what I have to Test
public async Task<IActionResult> RegisterAgencyUser([FromBody] Models.Request.RegisterAgencyUserRequest request)
{
JsonContentResult jsonContentResult = new JsonContentResult();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (string.IsNullOrEmpty(request.InvitationCode) || string.IsNullOrWhiteSpace(request.InvitationCode))
return Json(new SharedResponse(AppConstants.ServiceReponses.Error, "A user can't be registered without invitation"));
var invitationDetails = _inviteRepository.GetInvitaionCodeDetails(request.InvitationCode);
if (invitationDetails.Type != (int)InviteType.Agency)
{
return Json(new SharedResponse(AppConstants.ServiceReponses.Error, "Invalid invitation code"));
}
//...
You only mock dependencies to your class that you are testing. So you don't need to mock a POCO. Just set it up with dummy data like below:
var model = new RegisterAgencyUserRequest
{
Address = "Value1",
AgencyName = "Value2",
//...
};
and use it like below:
var controller = _accountController.RegisterAgencyUser(model);
Your dependency for the method is:
_inviteRepository.GetInvitaionCodeDetails()
so you need to mock the _inviteRepository and inject it into the subject controller under test when initializing.
You need to setup something on the mock, your code just creates an empty object.
var mock = new Mock<RegisterAgencyUserRequest>();
mock.Setup(x => x.Address).Returns(() => "some address");
RegisterAgencyUserRequest request = mock.Object;
Edit: The answer from Azhar Khorasany explains how it should be done, by not mocking the POCO, but the service/repository that you use in your test.
I've been following a regular pattern for unit testing my Web API methods using MOQ. This time I have a controller method that's a little different and I can't figure out why the test is failing.
Here's the standard look of one of our methods. We make a call to the repository and return OK.
API Method
[HttpPost]
public IHttpActionResult SampleMethod(SampleModel model)
{
var result= _myRepository.SampleMethod(model.Field1, model.Field2);
return Ok();
}
I usually use the following tests for something like this.
Unit Tests
/// <summary>
/// Tests the SampleMethod is run
/// </summary>
[TestMethod]
public void SampleMethod_Is_Run()
{
//Arrange
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>()))
.Returns(It.IsAny<EmailItem>()); //forgot to add this the first time
var controller = new MyController(mockRepository.Object);
//Act
controller.SampleMethod(It.IsAny<string>(), It.IsAny<string>());
//Assert
mockRepository.VerifyAll();
}
/// <summary>
/// Tests the SampleMethod returns correct status code
/// </summary>
[TestMethod]
public void SampleMethod_Returns_OK()
{
//Arrange
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>()))
.Returns(It.IsAny<EmailItem>()); //forgot to add this the first time;
var controller = new MyController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.SampleMethod(It.IsAny<string>(), It.IsAny<string>());
//Assert
Assert.IsInstanceOfType(response, typeof(OkResult));
}
Now let's say I have a method like this, which calls off to another class for sending an email. Why won't those unit tests work anymore?
New API Method
[HttpPost]
public IHttpActionResult SampleMethod(SampleModel model)
{
var emailItem= _myRepository.SampleMethod(model.Field1, model.Field2);
//With this additional code, the test will fail
EmailSender emailSender = new EmailSender();
emailSender.BuildEmail(emailItem.ToAddress, emailItem.Subject);
return Ok();
}
The error message on test fail I get is this, but there is no where to see extra exception information.
"System.Web.Http.HttpResponseException: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by 'Response' property of this exception for details."
you do setup you repository, but you don't return anything.
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>()));
You should try:
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>())).Returns(new EmailItem{ToAddress = "", Subject = ""});
you are trying to read
emailSender.BuildEmail(emailItem.ToAddress, emailItem.Subject);
Which you don't setup, so emailSender is null.
I am trying to test the following MVC controller Action, which is calling out to Web API for a List of Products:
public ActionResult Index()
{
var model = new List<Product>();
using(HttpClient client = new HttpClient())
{
model = client.GetAsync(uri).Result.Content.ReadAsAsync<List<Product>>().Result;
}
return View(model);
}
I am trying to unit test this, have tried using Telerik JustMock to test, for example:
[TestMethod]
public void IndexDisplaysAllProducts()
{ // Not sure how to call here
var repo = Mock.Arrange(() => (new List<Product>())).Returns(
new List<Product> {
new Product(),
new Product(),
new Product()
});
var controller = new ProductsController();
var result = (ViewResult)controller.Index();
var model = (List<Product>)result.Model;
Assert.AreEqual(3, model.Count);
}
Just wondered how you would go about testing this?
Mobile atm, so excuse brevity. Implement an httpclientfactory class and Ihttpclientfactory interface, inject that to the ctor using ioc then mock during test to create a mocked instance of the http client.
Alternatively, and even simpler to test, you could use this approach to implement a factory class for something that does all of the stuff you're using the http client for (GetAsync(uri).Result.Content.ReadAsAsync>() etc)
Hth
This is probably going to turn out to be a case of just needing another pair of eyes. I must be missing something, but I cannot figure out why this kind of thing cannot be tested for. I'm basically trying to ensure that unauthenticated users cannot access the view by marking the controller with the [Authorize] attribute and I'm trying to tests this using the following code:
[Fact]
public void ShouldRedirectToLoginForUnauthenticatedUsers()
{
var mockControllerContext = new Mock<ControllerContext>()
{ DefaultValue = DefaultValue.Mock };
var controller = new MyAdminController()
{ControllerContext = mockControllerContext.Object};
mockControllerContext.Setup(c =>
c.HttpContext.Request.IsAuthenticated).Returns(false);
var result = controller.Index();
Assert.IsAssignableFrom<RedirectResult>(result);
}
The RedirectResult I'm looking for is some kind of indication that the user is being redirected to the login form, but instead a ViewResult is always returned and when debugging I can see that the Index() method is successfully hit even though the user is not authenticated.
Am I doing something wrong? Testing at the wrong level? Should I rather be testing at the route level for this kind of thing?
I know that the [Authorize] attribute is working, because when I spin up the page, the login screen is indeed forced upon me - but how do I verify this in a test?
The controller and index method are very simple just so that I can verify the behaviour. I've included them for completeness:
[Authorize]
public class MyAdminController : Controller
{
public ActionResult Index()
{
return View();
}
}
Any help appreciated...
You are testing at the wrong level. The [Authorize] attribute ensures that the routing engine will never invoke that method for an unauthorized user - the RedirectResult will actually be coming from the route, not from your controller method.
Good news is - there's already test coverage for this (as part of the MVC framework source code), so I'd say you don't need to worry about it; just make sure your controller method does the right thing when it gets called, and trust the framework not to call it in the wrong circumstances.
EDIT: If you want to verify the presence of the attribute in your unit tests, you'll need to use reflection to inspect your controller methods as follows. This example will verify the presence of the Authorize attribute on the ChangePassword POST method in the 'New ASP.NET MVC 2 Project' demo that's installed with MVC2.
[TestFixture]
public class AccountControllerTests {
[Test]
public void Verify_ChangePassword_Method_Is_Decorated_With_Authorize_Attribute() {
var controller = new AccountController();
var type = controller.GetType();
var methodInfo = type.GetMethod("ChangePassword", new Type[] { typeof(ChangePasswordModel) });
var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
Assert.IsTrue(attributes.Any(), "No AuthorizeAttribute found on ChangePassword(ChangePasswordModel model) method");
}
}
Well you might be testing at the wrong level but its the test that makes sense. I mean, if I flag a method with the authorize(Roles="Superhero") attribute, I don't really need a test if I flagged it. What I (think I) want is to test that an unauthorized user doesn't have access and that an authorized user does.
For a unauthorized user a test like this:
// Arrange
var user = SetupUser(isAuthenticated, roles);
var controller = SetupController(user);
// Act
SomeHelper.Invoke(controller => controller.MyAction());
// Assert
Assert.AreEqual(401,
controller.ControllerContext.HttpContext.Response.StatusCode, "Status Code");
Well, it's not easy and it took me 10 hours, but here it is. I hope someone can benefit from it or convince me to go into another profession. :) (BTW - I'm using rhino mock)
[Test]
public void AuthenticatedNotIsUserRole_Should_RedirectToLogin()
{
// Arrange
var mocks = new MockRepository();
var controller = new FriendsController();
var httpContext = FakeHttpContext(mocks, true);
controller.ControllerContext = new ControllerContext
{
Controller = controller,
RequestContext = new RequestContext(httpContext, new RouteData())
};
httpContext.User.Expect(u => u.IsInRole("User")).Return(false);
mocks.ReplayAll();
// Act
var result =
controller.ActionInvoker.InvokeAction(controller.ControllerContext, "Index");
var statusCode = httpContext.Response.StatusCode;
// Assert
Assert.IsTrue(result, "Invoker Result");
Assert.AreEqual(401, statusCode, "Status Code");
mocks.VerifyAll();
}
Although, thats not very useful without this helper function:
public static HttpContextBase FakeHttpContext(MockRepository mocks, bool isAuthenticated)
{
var context = mocks.StrictMock<HttpContextBase>();
var request = mocks.StrictMock<HttpRequestBase>();
var response = mocks.StrictMock<HttpResponseBase>();
var session = mocks.StrictMock<HttpSessionStateBase>();
var server = mocks.StrictMock<HttpServerUtilityBase>();
var cachePolicy = mocks.Stub<HttpCachePolicyBase>();
var user = mocks.StrictMock<IPrincipal>();
var identity = mocks.StrictMock<IIdentity>();
var itemDictionary = new Dictionary<object, object>();
identity.Expect(id => id.IsAuthenticated).Return(isAuthenticated);
user.Expect(u => u.Identity).Return(identity).Repeat.Any();
context.Expect(c => c.User).PropertyBehavior();
context.User = user;
context.Expect(ctx => ctx.Items).Return(itemDictionary).Repeat.Any();
context.Expect(ctx => ctx.Request).Return(request).Repeat.Any();
context.Expect(ctx => ctx.Response).Return(response).Repeat.Any();
context.Expect(ctx => ctx.Session).Return(session).Repeat.Any();
context.Expect(ctx => ctx.Server).Return(server).Repeat.Any();
response.Expect(r => r.Cache).Return(cachePolicy).Repeat.Any();
response.Expect(r => r.StatusCode).PropertyBehavior();
return context;
}
So that gets you confirmation that users not in a role don't have access. I tried writing a test to confirm the opposite, but after two more hours of digging through mvc plumbing I will leave it to manual testers. (I bailed when I got to the VirtualPathProviderViewEngine class. WTF? I don't want anything to do a VirtualPath or a Provider or ViewEngine much the union of the three!)
I am curious as to why this is so hard in an allegedly "testable" framework.
Why not just use reflection to look for the [Authorize] attribute on the controller class and / or the action method you are testing? Assuming the framework does make sure the Attribute is honored, this would be the easiest thing to do.
I don't agree with Dylan's answer, because 'user must be logged in' does not imply that 'controller method is annotated with AuthorizeAttribute'
to ensure 'user must be logged in' when you call the action method, the ASP.NET MVC framework does something like this (just hold on, it will get simpler eventually)
let $filters = All associated filter attributes which implement
IAuthorizationFilter
let $invoker = instance of type ControllerActionInvoker
let $ctrlCtx = instance or mock of type ControllerContext
let $actionDesc = instance or mock of type ActionDescriptor
let $authzCtx = $invoker.InvokeAuthorizationFilters($ctrlCtx, $filters, $actionDesc);
then controller action is authorized when $authzCtx.Result is not null
It is hard to implement this pseudo script in a working c# code. Likely, Xania.AspNet.Simulator makes it really simple to setup a test like this and performs exactly these step under the cover. here is an example.
first install the package from nuget (version 1.4.0-beta4 at the time of writing)
PM > install-package Xania.AspNet.Simulator -Pre
Then your test method could look like this (assuming NUnit and FluentAssertions are installed):
[Test]
public void AnonymousUserIsNotAuthorized()
{
// arrange
var action = new ProfileController().Action(c => c.Index());
// act
var result = action.GetAuthorizationResult();
// assert
result.Should().NotBeNull();
}
[Test]
public void LoggedInUserIsAuthorized()
{
// arrange
var action = new ProfileController().Action(c => c.Index())
// simulate authenticated user
.Authenticate("user1", new []{"role1"});
// act
var result = action.GetAuthorizationResult();
// assert
result.Should().BeNull();
}
For .NET Framework we use this class to verify that every MVC and API Controller have AuthorizeAttribute and that every API Controller should have a RoutePrefixAttribute.
[TestFixture]
public class TestControllerHasAuthorizeRole
{
private static IEnumerable<Type> GetChildTypes<T>()
{
var types = typeof(Startup).Assembly.GetTypes();
return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
}
[Test]
public void MvcControllersShouldHaveAuthrorizeAttribute()
{
var controllers = GetChildTypes<Controller>();
foreach (var controller in controllers)
{
var authorizeAttribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Mvc.AuthorizeAttribute), true) as System.Web.Mvc.AuthorizeAttribute;
Assert.IsNotNull(authorizeAttribute, $"MVC-controller {controller.FullName} does not implement AuthorizeAttribute");
}
}
[Test]
public void ApiControllersShouldHaveAuthorizeAttribute()
{
var controllers = GetChildTypes<ApiController>();
foreach (var controller in controllers)
{
var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.AuthorizeAttribute), true) as System.Web.Http.AuthorizeAttribute;
Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement AuthorizeAttribute");
}
}
[Test]
public void ApiControllersShouldHaveRoutePrefixAttribute()
{
var controllers = GetChildTypes<ApiController>();
foreach (var controller in controllers)
{
var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.RoutePrefixAttribute), true) as System.Web.Http.RoutePrefixAttribute;
Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement RoutePrefixAttribute");
Assert.IsTrue(attribute.Prefix.StartsWith("api/", StringComparison.OrdinalIgnoreCase), $"API-controller {controller.FullName} does not have a route prefix that starts with api/");
}
}
}
It is a bit easier in .NET Core and .NET 5<. Here a MVC Controller inherits from Controller that in turn inherits from ControllerBase. An Api Controller inherits directly from ControllerBase and therefore we can test MVC and API Controllers using a single method:
public class AuthorizeAttributeTest
{
private static IEnumerable<Type> GetChildTypes<T>()
{
var types = typeof(Startup).Assembly.GetTypes();
return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
}
[Fact]
public void ApiAndMVCControllersShouldHaveAuthorizeAttribute()
{
var controllers = GetChildTypes<ControllerBase>();
foreach (var controller in controllers)
{
var attribute = Attribute.GetCustomAttribute(controller, typeof(Microsoft.AspNetCore.Authorization.AuthorizeAttribute), true) as Microsoft.AspNetCore.Authorization.AuthorizeAttribute;
Assert.NotNull(attribute);
}
}
}