我正在NUnit
项目的控制器端点上应用.NET Web API 2
集成测试,该项目的模型和控制器是通过Entity
code first from database
生成的。
我在思考应该测试控制器的哪些部分时遇到了麻烦。 最后,我们希望能够自动化“具有“ x”角色的用户能否获取此数据?”
查看此控制器的GET
部分,您将测试哪些部分以及您的推理是什么?
namespace api.Controllers.myNamespace
{
public class myController : ApiController
{
private string strUserName;
private string strError = "";
private string strApiName = "myTable";
private myDatabase db = new myDatabase();
// ----------------------------------------------------------------------
// GET: api/path
public IQueryable<myTable> GetmyTable()
{
try
{
this.strUserName = this.getUserName();
if
(
// ----- authorize -----
db.view_jnc_role_api_permission.Count
(
view =>
(
view.permission == "get"
&& view.apiName == this.strApiName
&& view.userName == this.strUserName
)
) == 1
// ----- /authorize -----
)
{
// ----- get -----
IQueryable<myTable> data =
from tbl in db.myTable
where tbl.deleted == null
select tbl;
// ----- /get -----
return data;
}
else
{
strError = "Unauthorized.";
throw new HttpResponseException(HttpStatusCode.Forbidden);
}
}
catch (Exception ex)
{
if (strError.Length == 0)
{
if (this.showException())
{
strError = ex.ToString();
}
}
throw new HttpResponseException(ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, strError));
}
}
}
供参考,这是我到目前为止的内容。我正在定义的一些私有字段不应在此处-当前试图通过AssemblyInfo.cs
从我的测试项目中访问私有方法来解决此问题:
namespace api.myNamespace
{
[TestFixture]
public class myController : ApiController
{
private string strUserName;
private string strError = "";
private string strApiName = "myTable";
private myDb db = new myDb();
// Using TransactionScope to (hopefully) prevent integration test's changes to database from persisting
protected TransactionScope TransactionScope;
// Instantiate _controller field
private myController _controller;
[SetUp]
public void SetUp() {
TransactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
// It's possible that one test may leave some state which could impact subsequent tests - so we must reinstantiate _controller at the start of each new test:
_controller = new myController();
}
[TearDown]
public void TearDown()
{
TransactionScope.Dispose();
}
**//------ TESTS -------//
// CanSetAndGetUserName
// AuthorizedUserCanGetData
// UnauthorizedUserCannotGetData
// AuthorizedUserCanPutData
// UnauthorizedUserCannotPutData
// AuthorizedUserCanPostData
// UnauthorizedUserCannotPostData
// AuthorizedUserCanDeleteData
// UnauthorizedUserCannotDeleteData**
[Test]
public void CanGetAndSetUsername()
{
// ARRANGE
var user = _controller.getUserName();
// ACT
// ASSERT
Assert.That(user, Is.EqualTo("my-internal-username"));
}
[Test]
public void UnauthorizedUserCannotGetData()
{
var user = "Mr Unauthorized";
// Unfinished bc integration testing is super abstract, subjective, hard, time consuming and hard. All downvoters are plebs.
Assert.That(user, Is.EqualTo());
}
}
}
}
答案 0 :(得分:1)
集成测试意味着几件事:
这是一个集成测试,因为它涉及到api和数据库的所有内容。
现在,您说您在确定要测试控制器的哪个部分时遇到了麻烦。这表明您将集成测试与单元测试混淆了。
我们已经介绍过的集成测试。 单元测试涵盖部分功能。您无需测试控制器,就不用管它了。
您真正需要考虑的是:
首先,将您的代码与控制器分开。保持控制器非常基础。它接收一个调用,验证请求模型,并将其进一步传递到功能发生的类库。这样您就可以忘记“测试控制器”,而专注于功能。单元测试将对您有所帮助,您的测试用例将变成这样
有了这样的设置,您可以按自己喜欢的方式设置测试数据并检查每个测试用例。
您想知道如何测试控制器的唯一原因是因为您将所有代码都转储到了控制器中,这当然会使一切变得困难。想想SOLID,想想SOC(关注点分离)。
一条建议:永远不要从端点返回IQueryable,这不是数据,仅仅是一个尚未运行的查询。返回列表,IEnumerable,单个对象,无论您需要什么,只需确保首先通过例如在IQueryable表达式上调用ToList()即可首先执行该列表。
因此,步骤如下: