How to Mock/Stub or simply ignore HttpRequest when unit testing [duplicate]

孤人 提交于 2019-12-02 06:36:17

To answer your question, you need the following in your test:

        var requestBase = new Mock<HttpRequestBase>();
        requestBase.Setup(r => r.ServerVariables)
               .Returns(new NameValueCollection { {"HTTP_X_REWRITE_URL", "your url"} });

        var httpContext = new Mock<HttpContextBase>();
        httpContext.Setup(x => x.Request).Returns(requestBase.Object);

        var ctrCtx = new Mock<ControllerContext>();
        ctrCtx.Setup(x => x.HttpContext).Returns(httpContext.Object);

        demoController.ControllerContext = ctrCtx.Object;  

However as @Mark suggested you don't need to create concrete instance of DeviceDetection inside your action, you need to inject it. But instead of injecting a concrete instance, it is better to wrap it into interface IDeviceDetector and inject this abstraction.

I will give a number of advantages:

  1. Your action does not depend on the implementation of DeviceDetection
  2. Mock<IDeviceDetection> allows you to raise exception on setup, to test exception handling of your try-catch block.
  3. You can assert that DetectDevice() method is called

Another suggestion - never use try{} catch(Exception ex){}, you should catch only those exceptions which you can handle. As you don't know which type of exception can be thrown and how to handle it effectively, e.g. it can be OutOfMemoryException. This article can give you basic ideas of different ways to handle exception in MVC.

UPDATE: As I see you are using Unity as IoC container. Unity has a possibility to inject constructor parameters. So again you need to extract an interface from DeviceDetector let's say IDeviceDetector. Register it

container.RegisterType<IDeviceDetector, DeviceDetector>(new InjectionConstructor(
HttpContext.Current.Request.ServerVariables["HTTP_X_REWRITE_URL"].ToString()));

Register DeviceDetector with TransientLifetimeManager.

Then your controller should look like

public class DemoController : Controller
{
    private readonly ICommonOperationsRepository _commonRepo;
    private readonly IDeviceDetection _deviceDetection;

    public DemoController (
        ICommonOperationsRepository commonRepo,
        IDeviceDetection deviceDetection)
    {
        _commonRepo = commonRepo;
        _deviceDetection = deviceDetection;
    }

    public ActionResult Default()
    {
        var model = new DemoModel();

        _deviceDetection.DetectDevice();
        model.ListTopListing.AddRange(_commonRepo.GetListings());

        return View(model);
    }
}

Note, in this case you need to write unit tests for your Unity container to verify that your injections are resolved correctly. Your unit test may look like:

[TestMethod]
public void Test()
{
    var repository = new Mock<ICommonOperationsRepository>();
    var deviceDetection = new Mock<IDeviceDetection>();

    var controller = new DemoController(repository.Object, deviceDetection.Object);
    controller.Default();

    deviceDetection.Verify(x => x.DetectDevice(), Times.Once());
}

Make DeviceDetection a concrete dependency of DemoController:

public class DemoController : Controller
{
    private readonly ICommonOperationsRepository _commonRepo;
    private readonly DeviceDetection dd;

    public DemoController (
        ICommonOperationsRepository commonRepo,
        DeviceDetection dd)
    {
        _commonRepo = commonRepo;
        this.dd = dd;
    }

    public ActionResult Default()
    {
        var model = new DemoModel();
        try
        {
            this.dd.DetectDevice();
            model.ListTopListing.AddRange(_commonRepo.GetListings());
        }
        catch (Exception ex)
        {
            ExceptionHandler objErr = new ExceptionHandler(ex, "DemoController .Default()\n Exception : " + ex.Message);
            objErr.LogException();
        }
        return View(model);
    }
}

This should enable you to create an instance of DemoController without relying on the Request property:

var sut = new DemoController(someStupRepository, new DeviceDetection("foo"));

You can do this in e.g. a unit test.

When you compose DemoController in your application, you pass in request.ServerVariables["HTTP_X_REWRITE_URL"].ToString() to DeviceDetection. You can get the request variable from the requestContext argument of CreateController.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!