5

我已经在单元测试中遇到了一些困难,我正在尝试在使用我目前正在从事的一个小项目时学习它,我遇到了这个问题这两个问题,我希望你能帮助我

1-我的项目是一个 MVC 项目。我的单元测试应该从哪个级别开始?他们应该只关注业务层吗?他们是否也应该在我的控制器上测试操作?

2-我有一种方法可以验证用户名格式,然后访问数据库以检查它是否可用。无论此用户名是否可用,返回都是布尔值。有人会为这种方法创建一个单元测试吗?我会对测试格式验证感兴趣,但是如何在不查询数据库的情况下检查它们?此外,如果格式正确,但用户名已被使用,我将得到一个错误值,但验证有效。我可以解耦这种方法,但是只有在格式正确的情况下才应该进行数据库验证,因此它们应该以某种方式绑定。具有单元测试知识的人将如何解决这个问题。或者有人将如何重构此方法以对其进行测试?我可以为数据库访问创建一个存根,但是如何在用户测试时将其附加到我的项目中,但在本地运行时将其分离?

谢谢!

4

2 回答 2

2

在您的特定情况下,您可以做的一件简单的事情是将您的验证方法分解为 3 种不同的方法:一种检查格式,一种检查数据库可用性,另一种将它们结合在一起。这将允许您单独测试每个子功能。

在更复杂的场景中,其他技术可能有用。本质上,这就是依赖注入控制反转派上用场的地方(不幸的是,这些短语对不同的人有不同的含义,但了解基本概念通常是一个好的开始)。

您的目标应该是将“检查此用户名是否可用”的概念与检查数据库的实现分离。

所以,而不是这个:

public class Validation
{
    public bool CheckUsername(string username)
    {
        bool isFormatValid = IsFormatValid(username);
        return isFormatValid && DB.CheckUsernameAvailability(username);
    }
}

你可以这样做:

public class Validation
{
    public bool CheckUsername(string username,
                              IUsernameAvailabilityChecker checker)
    {
        bool isFormatValid = IsFormatValid(username);
        return isFormatValid && checker.CheckUsernameAvailability(username);
    }
}

然后,从您的单元测试代码中,您可以创建一个自定义IUsernameAvailabilityChecker来执行您想要的任何测试目的。另一方面,实际的生产代码可以使用不同的实现IUsernameAvailabilityChecker实际查询数据库。

请记住,有很多很多技术可以解决这种测试问题,而且我给出的例子很简单而且做作。

于 2013-11-12T19:17:00.157 回答
1

可以使用模拟来完成针对外部服务的测试。如果您使用接口做得很好,那么模拟应用程序的各个部分非常容易。这些模拟可以注入您的单元并像通常处理它一样使用。

您应该尽快开始单元测试。如果您的应用程序不完整或缺少测试所需的代码,您仍然可以针对可以模拟的某些接口进行测试。

附带说明:单元测试是关于测试行为,并不是发现错误的有效方法。您会在测试中发现错误,但这不应该是您的目标。

例如:

interface UserService {
    public void setUserRepository(UserRepository userRepository);
    public boolean isUsernameAvailable(String username);
}


class MyUserService implements UserService {
    private UserRepository userRepository;

    public vois setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public boolean isUsernameAvailable(String username) {
        return userRepository.checkUsernameAvailability(username);
    }
}


interface UserRepository {
    public boolean checkUsernameAvailability(String username);
}

// The mock used for testing
class MockUserRepository {
    public boolean checkUsernameAvailability(String username) {
        if ("john".equals(username)) {
            return false;
        }
        return true;
    }
}

class MyUnitTest {
    public void testIfUserNotAvailable() {
        UserService service = new UserService();    
        service.setUserRepository(new MockUserRepository);

        assertFalse(service.isUsernameAvailable('john')); // yep, it's in use
    }

    public void testIfUserAvailable() {
        UserService service = new UserService();    
        service.setUserRepository(new MockUserRepository);

        assertTrue(service.isUsernameAvailable('mary')); // yep, is available
    }
}
于 2013-11-12T19:31:51.633 回答