【问题标题】:Unit test is passing when it should fail单元测试在它应该失败时通过
【发布时间】:2017-04-22 00:10:23
【问题描述】:

谁能看到我在下面的例子中哪里出错了。我正在编写测试以检查 ID 是否已存在。我有一个“UserService”,它调用通用存储库“RentalsRepository”中的方法,它为我调用数据。 *注意代码在系统中有效,只是在我的测试中无效

回购

public class RentalsRepository<T> : IRentalsRepository<T> where T : BaseClass
{
    private readonly RentalsDBContext _Context;
    private DbSet<T> entities;
    string errorMessage = string.Empty;

    public RentalsRepository(RentalsDBContext _Context)
    {
        this._Context = _Context;
        entities = _Context.Set<T>();
    }

    public T Get(string Id)
    {
        return entities.SingleOrDefault(e => e.Id == Id);
    } ...

用户服务

 public class UserService : IUserService {
private IRentalsRepository<UserAccount> _userRepository;

    public UserService(IRentalsRepository<UserAccount> userRepository)
    {
        this._userRepository = userRepository;
    }

    public UserAccount GetUserFromId(string id)
    {
        UserAccount user = _userRepository.Get(id);
        user.Email = Encryption.DecryptFromDatabase(user.Email);
        return user;
    }...

测试类

  [Fact]
    public void UserService_GetByID()
    {
        var users = new List<UserAccount> {
            new UserAccount { Id = "idstring1", Username = "username1"},
            new UserAccount { Id = "idstring2", Username = "username2" },
            new UserAccount { Id = "idstring3", Username = "username3" },
    }.AsQueryable();

        var mockSet = new Mock<DbSet<UserAccount>>();

        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Provider).Returns(users.Provider);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(users.Expression);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(users.ElementType);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator());

        var mockContext = new Mock<RentalsDBContext>();
        mockContext.Setup(m => m.Set<UserAccount>()).Returns(mockSet.Object);

        var mockRepo = new Mock<IRentalsRepository<UserAccount>>();
        mockRepo.Setup(m => m.Get(It.IsAny<string>())).Returns(mockSet.Object.FirstOrDefault());

        var service = new UserService(mockRepo.Object);
        UserAccount results = service.GetUserFromId("non-existant");

        Assert.Equal("idstring1", results.Id);
    } 

当我调试时,我可以看到在方法 public UserAccount GetUserFromId(string id) 中使用了值“不存在”,但它仍然以某种方式返回用户

最新尝试

        [Fact]
    public void UserService_GetUserByUsername()
    {
        byte[] b = Encryption.GetSalt();
        var users = new List<UserAccount> {
            new UserAccount { Id = "idstring2", Username = "username2" },
           new UserAccount { Id = "idstring3", Username = "username3" },
    }.AsQueryable();

        var mockContext = new Mock<RentalsDBContext>();
        var mockSet = new Mock<DbSet<UserAccount>>();

        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Provider).Returns(users.Provider);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(users.Expression);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(users.ElementType);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator());

        mockContext.Setup(m => m.Set<UserAccount>()).Returns(mockSet.Object);

        var mockRepo = new Mock<RentalsRepository<UserAccount>>(mockContext.Object);

        var testClass = new UserService(mockRepo.Object);
        UserAccount results = testClass.GetUserByUsername("username2");

        Assert.Equal("username1", results.Username);
    }

【问题讨论】:

    标签: c# .net unit-testing moq xunit


    【解决方案1】:
    mockRepo.Setup(m => m.Get(It.IsAny<string>())).Returns(mockSet.Object.FirstOrDefault());
    

    无论您将什么字符串传递给Get(),都将返回模拟集中的第一条记录(“idstring1”)。假设您将模拟的数据库上下文正确地放入存储库,则根本没有理由模拟 Get()

    话虽如此,如果您尝试测试 ID 是否已经存在,那是存储库的功能,而不是您的服务。您所做的所有服务都是解密电子邮件。您正在以这种方式测试存储库和服务,这不是单元测试。

    编辑

    我们回到您要测试什么的问题。如果您想测试存储库是否检索到正确的用户帐户,您可以模拟 db 上下文并使用真实的存储库类。

    [Fact]
    public void UserRepository_Get()
    {
        var users = new List<UserAccount> {
            new UserAccount { Id = "idstring2", Username = "username2" },
           new UserAccount { Id = "idstring3", Username = "username3" },
        }.AsQueryable();
    
        var mockSet = new Mock<DbSet<UserAccount>>();
    
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Provider).Returns(users.Provider);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(users.Expression);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(users.ElementType);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator());
    
        var mockContext = new Mock<RentalsDBContext>();
        mockContext.Setup(m => m.Set<UserAccount>()).Returns(mockSet.Object);
    
        var testClass = new RentalsRepository<userAccount>(mockContext.Object);
        var results = testClass.Get("username2");
    
        Assert.Equal("username2", results.Username);
    }
    

    如果您想测试用户服务检索用户并解密电子邮件,您将模拟存储库(它是 Get 函数)并使用真实的服务类。

    [Fact]
    public void UserService_GetUserByUsername()
    {
        var userAccount = new UserAccount { Id = "idstring2", Username = "username2", Email = "" };
    
        var mockRepo = new Mock<IRentalsRepository<UserAccount>>();
        mockRepo.Setup(m => m.Get("idstring2").Returns(userAccount);
    
        var testClass = new UserService(mockRepo.Object);
        var results = testClass.GetUserByUsername("idstring2");
    
        Assert.Equal("idstring2", results.Username);
        Assert.AreEqual("???", results.Email);
    }
    

    如果您想同时测试存储库和服务,当然可以,但它不会是单元测试,因为您要同时测试两件事。在这种情况下,您可以模拟 db 上下文并使用真实的存储库和服务类。

    [Fact]
    public void UserRepository_Get()
    {
        var users = new List<UserAccount> {
            new UserAccount { Id = "idstring2", Username = "username2" },
           new UserAccount { Id = "idstring3", Username = "username3" },
        }.AsQueryable();
    
        var mockSet = new Mock<DbSet<UserAccount>>();
    
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Provider).Returns(users.Provider);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.Expression).Returns(users.Expression);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.ElementType).Returns(users.ElementType);
        mockSet.As<IQueryable<UserAccount>>().Setup(m => m.GetEnumerator()).Returns(users.GetEnumerator());
    
        var mockContext = new Mock<RentalsDBContext>();
        mockContext.Setup(m => m.Set<UserAccount>()).Returns(mockSet.Object);
    
        var repository = new RentalsRepository<userAccount>(mockContext.Object);
        var service = new UserService(repository);
    
        var results = service.GetUserByUsername("username2");
    
        Assert.Equal("username2", results.Username);
    }
    

    【讨论】:

    • 我怎么知道我是否正确地将我的上下文传递到我的回购中?您能否详细说明您的意思是不需要模拟 Get() 。因此,根本没有理由测试该服务方法,因为 Encryption.DecryptFromDatabase(...) 将自行测试?谢谢!!
    • 没有必要模拟Get 如果您正在尝试测试检索不存在的记录。如果您希望 Service.GetuserFromId 成为真正的单元测试,您应该使用模拟的数据库上下文测试 Repository.Get,并使用模拟存储库对 Service.GetuserFromId 进行单独测试。
    • 据我所知,您可以将模拟的 db 上下文传递到 RentalsRepository 的构造函数中,以正确地将其放入存储库。
    • 你能看看我编辑的帖子吗?我的 results 值为 null 。谢谢! @paulabbott
    猜你喜欢
    • 2023-03-13
    • 2019-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-19
    相关资源
    最近更新 更多