Mock server in a Controller

2020-07-06 09:17发布

I have the following line in my controller:

string lTempPath = Path.Combine(Server.MapPath("~/Temp"), lRandomFileName);

Problem is Server is not virtual and can only be accessed with a getter.

I get a

"The method or operation is not implemented."

How can I mock this server?

The test I have created is as below:

    [TestCase]
    public void PreviewActionShouldGenerateUrlOfPdf()
    {
        //Arrange
        var server = MockRepository.GenerateMock<HttpServerUtilityBase>();
        server.Stub(s => s.MapPath("~Temp")).Return("~/Temp");
        var httpContext = MockRepository.GenerateMock<HttpContextBase>();
        httpContext.Stub(hc => hc.Server).Return(server);
        httpContext.Server.Stub(s => s.MapPath("~/Temp")).Return("~/Temp");

        var controller = new StudiesController()
        {
            ReportingService = MockRepository.GenerateMock<IReportingService>(),
            SecurityService = MockRepository.GenerateMock<ISecurityService>()
        };


        controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);

        controller.ReportingService.Stub(rs => rs.GetStudyByGID(new Guid())).Return(new Study());
        controller.ReportingService.Stub(rs => rs.ListPractices()).Return(new[] { new Practice(), new Practice() });
        controller.SecurityService.Stub(ss => ss.GetUser("")).IgnoreArguments().Return(new User());

        controller.ControllerContext.HttpContext = MockRepository.GeneratePartialMock<FakeHttpContext>("http://test.com");
        controller.HttpContext.User = new FakePrincipal(new FakeIdentity("test"), new string[0]);
        controller.ControllerContext.HttpContext.Stub(x => x.Request).Return(MockRepository.GenerateMock<HttpRequestBase>());
        controller.ControllerContext.HttpContext.Request.Stub(x => x.Url).Return(new Uri("http://test.com"));



        controller.ReportingService.Stub(
            rs =>
            rs.GenerateReport(new Study(), new Practice(), new User(), false, ReportGenerationOutputFormat.PDF)).IgnoreArguments().Return(new StudyReportSnapshot());

        var content = new ContentResult();

        //Act

        var result = (ContentResult)controller.Preview(new Guid());

        //Assert
        Assert.AreEqual(result.Content, content.Content);

    }

2条回答
ゆ 、 Hurt°
2楼-- · 2020-07-06 09:29

Assuming you're using some sort of IOC/DI container, you shouldn't depend on Controller.Server. Instead you should use HttpServerUtilityBase.

This example assumes Ninject as the IOC container, but any of the popular containers will do:

First, register HttpServerUtilityBase with your IOC container like so:

kernel.Bind<HttpServerUtilityBase>().ToMethod(c => new HttpServerUtilityWrapper(HttpContext.Current.Server));

This will make sure that at run time your app will use the current request's server property.

Then, add a constructor to your controller that accepts an instance of HttpServerUtilityBase:

public MyController(HttpServerUtilityBase server)
{
    this._server = server;
}

Now, anywhere before where you were calling Server.MapPath, just call _server.MapPath instead.

Finally, in your tests, you can mock HttpServerUtilityBase like so (assuming Moq as the mocking framework):

var server = new Mock<HttpServerUtilityBase>();
server.Setup(s => s.MapPath(It.IsAny<string>())).Returns<string>(s => /* set up how you want MapPath to behave here */);

EDIT

Since you mentioned you're not using an DI framework, you can resort to "Poor Man's Dependency Injection". Essentially adding an overloaded constructor:

public MyController()
    : this(new HttpServerUtilityWrapper(HttpContext.Current.Server))
{
}

public MyController(HttpServerUtilityBase server)
{
    this._server = server;
}

This will allow production code to use the current web request, but then you can create your own implementation of HttpServerUtilityBase to use for testing.

查看更多
Juvenile、少年°
3楼-- · 2020-07-06 09:34

You can use a Mocking framework in your Unit Test code such as Rhino.Mocks, Moq or FakeItEasy

An example unit test body would be (this example uses Moq)

var homeController = new HomeController();

//create mock of HttpServerUtilityBase
var server = new Mock<HttpServerUtilityBase>();

//set up mock to return known value on call.
server.Setup(x => x.MapPath("~/Temp")).Returns("c:\\temp\\");

var httpContext = new Mock<HttpContextBase>();

httpContext.Setup(x => x.Server).Returns(server.Object);

homeController.ControllerContext = new ControllerContext(httpContext.Object, new RouteData(), homeController);

YourModelName yourModelName = new YourModelName();
yourModelName.lRandomFileName = "zzzz.so";

var result = homeController.YourActionName(yourModelName);

Then you can assert the result.

Hope that gives you a few ideas

查看更多
登录 后发表回答