gpt4 book ai didi

c# - 具有 IHttpActionResult 返回类型和映射数据的方法的 Asp.net 单元测试

转载 作者:行者123 更新时间:2023-11-30 23:07:17 24 4
gpt4 key购买 nike

你好,我需要测试实现 ApiController 的 APIcontroller,但我不知道该怎么做,我知道 UnitTesting 的基础知识,但这对我来说有点太复杂了。另外我不知道如何在单元测试中使用 automapper,所以也许有人可以帮我解决这个问题

这是我的 Controller :

   namespace Vidly.Controllers.Api
{
public class CustomersController : ApiController
{
private ApplicationDbContext _context;

public CustomersController(ApplicationDbContext _context)
{
_context = new ApplicationDbContext();
}

// GET /api/customers
public IHttpActionResult GetCustomers(string query = null)
{
var customersQuery = _context.Customers
.Include(c => c.MembershipType);

if (!String.IsNullOrWhiteSpace(query))
customersQuery = customersQuery.Where(c => c.Name.Contains(query));

var customerDtos = customersQuery
.ToList()
.Select(Mapper.Map<Customer, CustomerDto>);

return Ok(customerDtos);
}

// GET /api/customers/1
public IHttpActionResult GetCustomer(int id)
{
var customer = _context.Customers.SingleOrDefault(c => c.Id == id);

if (customer == null)
return NotFound();

return Ok(Mapper.Map<Customer, CustomerDto>(customer));
}

// POST /api/customers
[HttpPost]
public IHttpActionResult CreateCustomer(CustomerDto customerDto)
{
if (!ModelState.IsValid)
return BadRequest();

var customer = Mapper.Map<CustomerDto, Customer>(customerDto);
_context.Customers.Add(customer);
_context.SaveChanges();

customerDto.Id = customer.Id;
return Created(new Uri(Request.RequestUri + "/" + customer.Id), customerDto);
}

// PUT /api/customers/1
[HttpPut]
public IHttpActionResult UpdateCustomer(int id, CustomerDto customerDto)
{
if (!ModelState.IsValid)
return BadRequest();

var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);

if (customerInDb == null)
return NotFound();

Mapper.Map(customerDto, customerInDb);

_context.SaveChanges();

return Ok();
}

// DELETE /api/customers/1
[HttpDelete]
public IHttpActionResult DeleteCustomer(int id)
{
var customerInDb = _context.Customers.SingleOrDefault(c => c.Id == id);

if (customerInDb == null)
return NotFound();

_context.Customers.Remove(customerInDb);
_context.SaveChanges();

return Ok();
}
}
}

那么我需要如何开始,我是否需要像这样的某种接口(interface)来模拟 dbcontext:

public interface IAPICustomerRepository
{

IHttpActionResult GetCustomers(string query = null);
IHttpActionResult GetCustomer(int id);
IHttpActionResult CreateCustomer(CustomerDto customerDto);
IHttpActionResult UpdateCustomer(int id, CustomerDto customerDto);
IHttpActionResult DeleteCustomer(int id);
}

或者也许我可以在没有模拟的情况下编写单元测试。

更新在我根据 Nkosi 的建议编辑我的代码后,我收到了这些错误

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
An error occurred when trying to create a controller of type 'CustomersController'. Make sure that the controller has a parameterless public constructor.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace>
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'Vidly.Controllers.Api.CustomersController' does not have a default constructor
</ExceptionMessage>
<ExceptionType>System.ArgumentException</ExceptionType>
<StackTrace>
at System.Linq.Expressions.Expression.New(Type type)
at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
</InnerException>
</Error>

然后我创建一个默认构造函数(据我所知没有参数)然后我得到另一个错误:

 <Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Object reference not set to an instance of an object.
</ExceptionMessage>
<ExceptionType>System.NullReferenceException</ExceptionType>
<StackTrace>
at Vidly.Controllers.Api.CustomersController.GetCustomers(String query) in C:\Users\Dovydas Petrutis\Desktop\vidly-mvc-5-master\Vidly\Controllers\Api\CustomersController.cs:line 26
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[]
methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Dispatcher.HttpControllerDispatcher <SendAsync>d__1.MoveNext()
</StackTrace>
</Error>

现在问题出在哪里?

最佳答案

首先创建一个公开你想要的功能的服务,这样它就可以很容易地被替换/模拟以进行测试并提高可维护性。

当您提到存储库时,您已经很接近了。 IHttpActionResult 是一个 UI 问题,因此您可以像这样重构您的界面。

public interface IAPICustomerRepository {
IEnumerable<CustomerDto> GetCustomers(string query = null);
CustomerDto GetCustomer(int id);
int CreateCustomer(CustomerDto customerDto);
CustomerDto UpdateCustomer(int id, CustomerDto customerDto);
bool? DeleteCustomer(int id);
}

Controller 现在在功能方面将更加精简,不再关心 EF/DbContext 或 Automapper。

public class CustomersController : ApiController {
private IAPICustomerRepository repository;

public CustomersController(IAPICustomerRepository repository) {
this.repository = repository;
}

// GET /api/customers
public IHttpActionResult GetCustomers(string query = null) {
var customerDtos = repository.GetCustomers(query);
return Ok(customerDtos);
}

// GET /api/customers/1
public IHttpActionResult GetCustomer(int id) {
var customer = repository.GetCustomer(id);
if (customer == null)
return NotFound();

return Ok(customer);
}

// POST /api/customers
[HttpPost]
public IHttpActionResult CreateCustomer([FromBody]CustomerDto customerDto) {
if (!ModelState.IsValid)
return BadRequest();
var id = repository.CreateCustomer(customerDto);
customerDto.Id = id;
return Created(new Uri(Request.RequestUri + "/" + id), customerDto);
}

// PUT /api/customers/1
[HttpPut]
public IHttpActionResult UpdateCustomer(int id, [FromBody]CustomerDto customerDto) {
if (!ModelState.IsValid)
return BadRequest();

var updated = repository.UpdateCustomer(id, customerDto);
if (updated == null)
return NotFound();

return Ok();
}

// DELETE /api/customers/1
[HttpDelete]
public IHttpActionResult DeleteCustomer(int id) {
var deleted = repository.DeleteCustomer(id);
if (deleted == null)
return NotFound();

return Ok();
}
}

随着 Controller 现在依赖于抽象,您可以在单独测试 Controller 时模拟功能。

Automapper 是一个实现问题,可以封装在抽象之后,因此在测试时它不是问题。

以下示例使用 Moq 模拟框架。您可以使用自己选择的框架。

[TestClass]
public class CustomersController_Should {
[TestMethod]
public void GetCustomers() {
//Arrange
var fakeCustomers = new List<CustomerDto>{
new CustomerDto{ Id = 1 }
};
var repository = new Mock<IAPICustomerRepository>();
repository
.Setup(_ => _.GetCustomers(It.IsAny<string>()))
.Returns(fakeCustomers)
.Verifiable();

var controller = new CustomersController(repository.Object);

//Act
var result = controller.GetCustomers();

//Assert
repository.Verify();
//..other assertions
}

//...Other tests
}

最初在 Controller 中的功能将被封装在生产中存储库的实现中。

关于c# - 具有 IHttpActionResult 返回类型和映射数据的方法的 Asp.net 单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47458253/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com