You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC应用测试优化咨询:能否用真实数据库测试登录方法?

问题

我已经为IUserService和IDataResult创建了Mock对象,当前测试可以正常运行,但测试getcustomerlogin方法时并没有从数据库获取数据,而是用了手动创建的User对象。想问下有没有更优的测试方案?能不能用数据库里的真实数据来测试这个方法?


当前测试代码

namespace UnitTesting
{
    public class Tests
    {
        [Test]
        public void login_unit_test()
        {
            // arrange
            var userinput = new UserForLogin()
            {
                email = "testmail@mail.com",
                password = "123456"
            };

            var userobject = new User()
            {
                Email= "testmail@mail.com",
                Password = "123456"
            };

            var mockIdataResult = new Mock<IDataResult<User>>();
            mockIdataResult.Setup(i => i.Success).Returns(true);
            mockIdataResult.Setup(i => i.Data).Returns(userobject);

            var mockIUserService = new Mock<IUserService>();
            mockIUserService.Setup(i => i.getByEmail(userinput)).Returns(mockIdataResult.Object);
            var authscontroller = new AuthsController(mockIUserService.Object);

            // action
            IActionResult result = authscontroller.getcustomerlogin(userinput);
            var okResult = result as OkObjectResult;

            // assert
            Assert.AreEqual(200, okResult.StatusCode);
        }
    }
}

待测试的AuthsController

namespace WEBAPII.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthsController : ControllerBase
    {
        IUserService _userService;

        public AuthsController(IUserService userService)
        {
            _userService = userService;
        }

        [HttpPost("login")]
        public IActionResult getcustomerlogin(UserForLogin userForLogin)
        {
            var user = _userService.getByEmail(userForLogin);

            if (user.Success)
            {
                if (!(user.Data.Email == userForLogin.email && 
                      user.Data.Password == userForLogin.password))
                {
                    return BadRequest(user);
                }

                return Ok(user);
            }

            return BadRequest(user);
        }

        [HttpPost("logindadmin")]
        public IActionResult adminlogin(UserForLogin userForLogin)
        {
            var admin = _userService.getAdmin(userForLogin);

            if (admin.Success)
            {
                return Ok(admin);
            }

            return BadRequest(admin);
        }
    }
}

依赖的IUserService接口

namespace Business.Abstract
{
    public interface IUserService
    {
        List<User> GetAll();
        User GetById(int userId);
        void Add(User user);

        IDataResult<User> getByEmail(UserForLogin userForLogin);
        IDataResult<User> getAdmin(UserForLogin userForLogin);
    }
}

UserForLogin类

public class UserForLogin
{
    public string email { get; set; }
    public string password { get; set; }
}

User实体类

namespace Entities.Concrete
{
    public class User : IEntity
    {
        public int id { get; set; }
        public string Email { get; set; }

        public string Password { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }

        public DateTime CreatedAt { get; set; }

        public string roles { get; set; }
    }
}

解答

一、更优的单元测试方案

你当前的测试只覆盖了“正确账号密码返回200”这一种场景,可通过补充边界场景、优化Mock逻辑来提升测试覆盖率:

  • 补充场景覆盖:
    • 账号正确但密码错误:MockgetByEmail返回正确用户,输入密码与用户密码不一致,断言返回BadRequest
    • 用户不存在:MockgetByEmail返回Success=false的IDataResult,断言返回BadRequest
    • 无效输入:构造空邮箱、格式错误的邮箱输入,验证控制器的处理逻辑(若有校验)
  • 优化Mock匹配:当前用对象引用匹配getByEmail参数,建议用条件匹配(如It.Is<UserForLogin>(u => u.email == "testmail@mail.com")),避免因对象实例不同导致Mock失效

示例优化测试代码:

[Test]
public void login_with_wrong_password_returns_badrequest()
{
    // arrange
    var userinput = new UserForLogin()
    {
        email = "testmail@mail.com",
        password = "wrongpass"
    };

    var userobject = new User()
    {
        Email= "testmail@mail.com",
        Password = "123456"
    };

    var mockIdataResult = new Mock<IDataResult<User>>();
    mockIdataResult.Setup(i => i.Success).Returns(true);
    mockIdataResult.Setup(i => i.Data).Returns(userobject);

    var mockIUserService = new Mock<IUserService>();
    // 按邮箱条件匹配,而非对象引用
    mockIUserService.Setup(i => i.getByEmail(It.Is<UserForLogin>(u => u.email == userinput.email)))
                    .Returns(mockIdataResult.Object);
    var authscontroller = new AuthsController(mockIUserService.Object);

    // action
    var result = authscontroller.getcustomerlogin(userinput);

    // assert
    Assert.IsInstanceOf<BadRequestObjectResult>(result);
}

二、使用真实数据库数据测试(集成测试)

如果要验证从数据库取数到控制器响应的完整链路,需要编写集成测试,步骤如下:

  1. 准备隔离测试库:创建专门的测试数据库(与生产库隔离),每次测试前初始化数据(如用EF Core的EnsureCreated+种子数据,或事务回滚)
  2. 注入真实依赖:不MockIUserService,注入其真实实现,并关联测试数据库上下文
  3. 编写测试用例:测试前插入已知用户数据,调用控制器方法后断言结果,测试后清理数据

示例集成测试结构(基于xUnit+EF Core):

public class AuthsControllerIntegrationTests : IDisposable
{
    private readonly DbContextOptions<YourDbContext> _dbContextOptions;
    private readonly YourDbContext _context;
    private readonly AuthsController _controller;

    public AuthsControllerIntegrationTests()
    {
        // 使用内存数据库作为测试载体(也可使用真实SQL Server/MySQL)
        _dbContextOptions = new DbContextOptionsBuilder<YourDbContext>()
            .UseInMemoryDatabase(databaseName: "TestAuthDb")
            .Options;

        _context = new YourDbContext(_dbContextOptions);
        // 初始化测试数据
        _context.Users.Add(new User
        {
            Email = "test@example.com",
            Password = "correctpass",
            FirstName = "Test",
            LastName = "User",
            CreatedAt = DateTime.Now,
            roles = "Customer"
        });
        _context.SaveChanges();

        // 注入真实服务
        var userService = new UserService(_context);
        _controller = new AuthsController(userService);
    }

    [Fact]
    public void getcustomerlogin_with_valid_credentials_returns_ok()
    {
        // arrange
        var loginInput = new UserForLogin
        {
            email = "test@example.com",
            password = "correctpass"
        };

        // action
        var result = _controller.getcustomerlogin(loginInput);

        // assert
        var okResult = Assert.IsType<OkObjectResult>(result);
        var dataResult = Assert.IsType<IDataResult<User>>(okResult.Value);
        Assert.True(dataResult.Success);
        Assert.Equal("test@example.com", dataResult.Data.Email);
    }

    public void Dispose()
    {
        // 清理测试数据库
        _context.Database.EnsureDeleted();
        _context.Dispose();
    }
}

三、测试方案选择建议

  • 单元测试:速度快、隔离性强,适合覆盖控制器所有逻辑分支,无需依赖外部资源
  • 集成测试:验证端到端流程正确性,能发现单元测试遗漏的问题(如数据库查询错误),但运行速度慢,需维护测试环境

建议两种测试结合:用单元测试覆盖所有逻辑分支,用集成测试验证核心流程的完整性。

内容的提问来源于stack exchange,提问作者totames

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 10:50:24