如何通过数据库访问对方法进行单元测试?

时间:2013-11-12 18:59:53

标签: unit-testing

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

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

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

谢谢!

2 个答案:

答案 0 :(得分:2)

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

在更复杂的场景中,其他技术可能会有用。从本质上讲,这是dependency injectioninversion of control派上用场的地方(不幸的是,这些短语对不同的人来说意味着不同的东西,但获得基本的想法通常是一个好的开始)。

您的目标应该是从检查数据库的实施中解除“检查此用户名是否可用”的概念。

所以,而不是:

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实际查询数据库的不同实现。

请记住,有许多许多技术可以解决这种测试问题,我给出的示例很简单,也很有用。

答案 1 :(得分: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
    }
}