我熟悉TDD的基本原则:
但是,我对接口和实现适合的位置感到困惑。我正在业余时间创建一个Spring Web应用程序,而不是用枪支开发,我想了解如何更好地测试接口/实现,采用我在这里创建的简单示例代码:< / p>
public class RunMe
{
public static void main(String[] args)
{
// Using a dummy service now, but would have a real implementation later (fetch from DB etc.)
UserService userService = new DummyUserService();
System.out.println(userService.getUserById(1));
}
}
interface UserService
{
public String getUserById(Integer id);
}
class DummyUserService implements UserService
{
@Override
public String getUserById(Integer id)
{
return "James";
}
}
我已经创建了UserService
接口,最终会有一个真实的实现,它将查询数据库,但为了让应用程序开始,我已经替换了DummyUserService
实现只会返回一些静态数据。
问题:如何实施上述测试策略?
我可以创建一个名为DummyUserServiceTest
的测试类,并测试当我调用getUserById()
它将返回James
时,如果不浪费时间(?),这似乎很简单。< / p>
随后,我还可以创建一个测试类RealUserService
来测试getUserById()
从数据库返回用户名。这个部分让我感到困惑,这样做,这实际上是不是超越了单元测试的边界,而是变成了一个整合测试(点击数据库)?
问题(改进,一点点):当使用具有虚拟/存根的接口和实际实现时,哪些部分应该进行单元测试,哪些部分可以安全地保持未经测试?
昨晚我花了几个小时搜索这个主题,并且大多发现了关于TDD的教程,或者如何使用JUnit的示例,但在建议实际应该测试的内容中没有任何内容。完全有可能,我没有足够的搜索或者没有找到合适的东西......
答案 0 :(得分:4)
不要测试虚拟实现:它们不会在生产中使用。测试它们没有任何意义。
如果真正的UserService实现除了转到数据库并通过其ID获取用户名之外什么都不做,那么测试应该测试它是否正确并且正确执行。如果您愿意,可以将其称为集成测试,但它仍然是一个应该编写并自动化的测试。
通常的策略是在@Before带注释的测试方法中使用最少的测试数据填充数据库,并让您测试方法检查对于数据库中存在的ID,返回相应的用户名。
答案 1 :(得分:3)
我建议你先阅读本书:{4}由Steve Freemand和Nat Pryce阅读。它回答了你的问题以及与TDD相关的许多其他问题。
在您的特定情况下,您应该使用数据库适配器配置RealUserService
,这将进行真正的数据库查询。服务本身将是服务,而不是数据持久性。阅读这本书,它会有很多帮助:)
答案 2 :(得分:0)
JB的答案很好,我以为我会抛弃我用过的另一种技术。
在开发原始测试时,首先不要打扰UserService
。事实上,继续写下真实的东西。继续Kent Beck's 3 rules。
1)让它工作。 2)做对。 3)快点。
您的代码将进行测试,然后通过ID验证查找工作。正如JB所述,此时您的测试将被视为集成测试。一旦他们通过我们已成功完成第1步。现在,看看设计。这样对吗?调整任何设计气味并检查列表中的第2步。
对于第3步,我们需要快速进行此测试。我们都知道,所有事务管理和数据库设置的集成测试都很慢且容易出错。一旦我们知道代码有效,我通常不会对集成测试感到烦恼。正是在这个时候,您可以介绍您的虚拟服务,有效地将您的集成测试转变为单元测试。现在它没有以任何方式触及数据库,我们可以从列表中检查第3步,因为此测试现在很快。
那么,这种方法有什么问题?好吧,很多人会说我仍然需要对数据库支持的UserService
进行测试。我通常不会在我的项目中保留集成测试。我的观点是,这些类型的测试都很慢,很脆弱,并且在大多数项目中没有捕获到足够的逻辑错误来为自己付费。
希望有所帮助!
布兰登