简体   繁体   English

使用 xUnit/Moq 进行控制器 API 测试 - 控制器为空

[英]Controller API Testing with xUnit/Moq - Controller is null

I'm new to unit testing, so my problem is probably with my code and not the Moq framework, but here goes.我是单元测试的新手,所以我的问题可能出在我的代码而不是 Moq 框架上,但这里是。

I'm using .Net Core with xUnit and the Moq framework, and I'm more or less following instructions from their documentation .我将 .Net Core 与 xUnit 和 Moq 框架一起使用,我或多或少地遵循他们文档中的说明 I'm trying to test route api/user to get all users, and the issue was on asserting that the response was an ObjectResult containing <IEnumerable<User>> .我正在尝试测试路由api/user以获取所有用户,问题在于断言响应是包含<IEnumerable<User>>ObjectResult No matter what I tried, result.Value was always null.无论我尝试什么, result.Value始终为空。 The first assertion passes fine.第一个断言通过得很好。

I set up a console project to debug this, and found something interesting.我设置了一个控制台项目来调试它,并发现了一些有趣的东西。 that value of the controller in the test method in Visual Studio is null. Visual Studio 中测试方法中控制器的值为空。 In VS Code, the value in the debugger shows Unknown Error: 0x00000... .在 VS Code 中,调试器中的值显示Unknown Error: 0x00000...

Below is the test:下面是测试:

public class UserControllerTests {

    [Fact]
    public void GetAll_ReturnsObjectResult_WithAListOfUsers() {
        // Arrange
        var mockService = new Mock<IUserService>();
        var mockRequest = new Mock<IServiceRequest>();
        mockService.Setup(svc => svc.GetUsers(mockRequest.Object))
            .Returns(new ServiceRequest(new List<User> { new User() }));
        var controller = new UserController(mockService.Object);

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

        // Assert
        Assert.IsType<ObjectResult>(result);
        Assert.IsAssignableFrom<IEnumerable<User>>(((ObjectResult)result).Value);
    }

}

And here is the controller:这是控制器:

public class UserController : Controller {

    private IUserService service;

    public UserController(IUserService service) {
        this.service = service;
    }

    [HttpGet]
    public IActionResult GetAll() {
        var req = new ServiceRequest();
        service.GetUsers(req);

        if(req.Errors != null) return new BadRequestObjectResult(req.Errors);
        return new ObjectResult(req.EntityCollection);
    }

}

And the Service Layer:和服务层:

public interface IUserService {
    IServiceRequest GetUsers(IServiceRequest req);
}    
public class UserService : IUserService {       
    private IUserRepository repo;       
    public IServiceRequest GetUsers(IServiceRequest req) {
        IEnumerable<User> users = null;         
        try {
            users = repo.GetAll();
        }
        catch(MySqlException ex) {
            req.AddError(new Error { Code = (int)ex.Number, Message = ex.Message });
        }
        finally {
            req.EntityCollection = users;
        }
        return req;
    }
}

public interface IServiceRequest {
    IEnumerable<Object> EntityCollection { get; set; }
    List<Error> Errors { get; }
    void AddError(Error error);
}
public class ServiceRequest : IServiceRequest {
    public IEnumerable<Object> EntityCollection { get; set; }
    public virtual List<Error> Errors { get; private set; }

    public ServiceRequest () { }

    public void AddError(Error error) {
        if(this.Errors == null) this.Errors = new List<Error>();
        this.Errors.Add(error);
    }       
}

Like I said, it's probably something I'm doing wrong, I'm thinking in the mockService.Setup() but I'm not sure where.就像我说的,这可能是我做错了,我在mockService.Setup()思考,但我不确定在哪里。 Help please?请帮忙?

From the use of service.GetUsers(req) it looks like service is suppose to populate the service request but in your setup you have it returning a service request.从使用的service.GetUsers(req)它看起来像service是假设填充服务请求,但在你的安装你有它返回一个服务请求。 A result which is also not used according to your code.根据您的代码也未使用的结果。

You need a Callback to populate whatever parameter is given to the service in order to mock/replicate when it is invoked.您需要一个Callback来填充提供给服务的任何参数,以便在调用时模拟/复制。 Since the parameter is being created inside of the method you will use Moq's It.IsAny<> to allow the mock to accept any parameter that is passed.由于参数是在方法内部创建的,您将使用 Moq 的It.IsAny<>来允许模拟接受传递的任何参数。

var mockService = new Mock<IUserService>();
mockService.Setup(svc => svc.GetUsers(It.IsAny<IServiceRequest>()))
    .Callback((IServiceRequest arg) => {
        arg.EntityCollection = new List<User> { new User() };
    });

This should allow the method under test to flow through it's invocation and allow you to assert the outcome.这应该允许被测方法流过它的调用并允许您断言结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM