C#单元测试API 2调用

本文关键字:调用 API 单元测试 | 更新日期: 2023-09-27 18:23:40

我有一个web api 2 web服务get方法。在内部,我使用的是HttpContext.Current.Request.UserHostAddress。当在单元测试中直接调用我的控制器方法时,这是不填写的,空对象的错误也是如此。因此,我搜索了如何填写这一信息,并发现以下内容有助于解决该问题:将IP地址添加到HttpRequestMessage

但是,这需要一个服务器名称来向其发送请求。问题是,当测试运行时,需要为此API web服务运行VSExpress,而仅运行测试时不会运行。最重要的是,即使看起来是这样,它也会选择一个随机端口来运行,这样我就不能像他在上面的链接中那样硬编码地址了。鉴于上述问题,我如何测试我的api 2方法?

这是我刚测试api方法时弹出的一行

string ip = HttpContext.Current.Request.UserHostAddress;

[EDIT]答案

只是为了让每个人都知道这里是代码的解决方案

public class MyController : ApiController
{
    private: HttpRequestBase httpRequest;
    public MyController()
    {
        httpRequest = new HttpRequestWrapper(HttpContext.Current.Request)
    }
    public MyController(HttpRequestBase http)
    {
        httpRequest = http;
    }
    public HttpResponseMessage Get()
    {
        string ip = httpRequest.UserHostAddress;
    }
}

我在单元测试中使用Moq:

Mock<HttpRequestBase> httpRequestMock = new Mock<HttpRequestBase>();
httpRequestMock.Setup(x => x.UserHostAddress).Returns("127.0.0.1");
// then pass httpRequestMock.Object to my controller ctor and good to go

C#单元测试API 2调用

将控制器与HTTP上下文解耦。可能有一些我不熟悉的内置功能可以做到这一点,但一种方法是简单地注入一个可模拟的对象。考虑一下这样的东西:

public interface IRequestInformation
{
    string UserHostAddress { get; }
}
public class RequestInformation : IRequestInformation
{
    public string UserHostAddress
    {
        get { return HttpContext.Current.Request.UserHostAddress; }
    }
}

现在,您已经抽象了接口后面对HttpContext的依赖关系。如果您正在使用依赖项注入,请将该接口注入到您的控制器中。如果你不是,你可以伪造:

// in your controller...
private IRequestInformation _request;
public IRequestInformation RequestInfo
{
    get
    {
        if (_request == null)
            _request = new RequestInformation();
        return _request;
    }
    set { _request = value; }
}

然后在你的控制器逻辑中使用它:

string ip = RequestInfo.UserHostAddress;

现在,在单元测试中,您可以提供mock/fake/etc。对于CCD_ 2属性。可以手动创建一个,也可以使用模拟库。如果你手动创建一个,那就足够简单了:

public class RequestInformationFake : IRequestInformation
{
    public string UserHostAddress
    {
        get { return "some known value"; }
    }
}

然后在安排测试时将其提供给控制器:

var controller = new YourController();
controller.RequestInformation = new RequestInformationFake();
// run your test

将对HttpContext的引用替换为对HttpContextBase的引用。在代码中,使用HttpContextWrapper实例初始化HttpContextBase,这是web堆栈中的默认行为实现。

但是,在您的测试中注入一个自定义的HttpContextBase实现,您可以在其中实现测试所需的方法和行为。

如链接中所述:

HttpContextBase类是一个抽象类,包含成员作为HttpContext类。HttpContextBase类启用您可以创建类似HttpContext类的派生类,但是可以自定义并在ASP.NET管道之外工作的。执行单元测试时,通常使用派生类使用满足场景的自定义行为实现成员你正在测试。

将以下方法添加到控制器中,或注入等效方法。它使用神奇的字符串MS_HttpContext,因为这正是AspNetWebStack实现用于完全相同目的的字符串。

HttpContextBase HttpContextBase => HttpContext.Current != null
    ? new HttpContextWrapper(HttpContext.Current)
    : (HttpContextBase)Request.Properties["MS_HttpContext"]

将控制器中HttpContext.Current的所有其他用途替换为HttpContextBase

单元测试时:

var context = new Mock<HttpContextBase>();
...
controller.Request = new HttpRequestMessage();
controller.Request.Properties["MS_HttpContext"] = context.Object;