2009-06-25 61 views
3

我试图提高我的TDD/OO技能,并且每次尝试使用TDD来影响设计时,我都会碰到从哪里开始的墙。用TDD思考OO - 从哪里开始?

这里是我的使用案例/故事:

找出到期复审客户的一个子集。为他们开始审查并发送几封信。

现在我的肌肉记忆已经打开了一个查询窗口,编写了查询,设计了一个UI,然后我必须编写一些代码将这些位粘合在一起。

我希望域代码成为焦点,我希望它在测试中。

那么在这种情况下最简单的事情是什么?

我想我想要我的客户名单。我已经有了一个客户端对象(CSLA风格),尽管这有一个很难破解的依赖关系。我想我可以有一个ClientReviewClients对象,并测试我得到正确数量的评论。我需要考虑的因素有很多,所以看起来并不简单。无论如何,我如何嘲笑20个客户中有10条评论的事实?

任何人都可以在我的路上帮助我吗?

回答

4

这里 - 我会在您关闭了几个测试:

class IdentifyClientsDueForReview { 
    public void CanStartSearch() { 
     var s = new ClientSearcher(); 
    } 

    public void CanSearchClients() { 
     var s = new ClientSearcher(); 
     var r = s.Find(c => c.Id == 1); 
     Assert.IsNotNull(r); 
    } 

    public void Finds10Clients() { 
     var db = new MockDB(); 
     // Clients that need review 
     for (int i = 0; i < 10; i++) { 
     db.Add(new Client() { 
      NextReview = DateTime.Today.SubtractDays(i) 
     }); 
     } 
     // Clients that don't need review 
     for (int i = 0; i < 10; i++) { 
     db.Add(new Client() { 
      NextReview = DateTime.Today.AddDays(i) 
     }); 
     } 

     var s = new ClientSearcher(db); 
     var r = s.Find(c => c.NextReview <= DateTime.Today); 
     Assert.AreEqual(10, r.Count); 
    } 
} 

这是用的LINQ to SQL或考虑类似的后端ORM建 - 否则,你可能沟Find方法有一些硬编码FindBy<Criteria>方法。

这应该给你ClientSearcher类,它使用一个接口来命中数据库。 MockDBRealDB类都将实现该接口。

+0

有很多东西在那里。一个类来搜索客户。我想我会把它放在像客户对象这样的神。 – 2009-06-25 11:35:50

+0

CanStartSearch在那里做什么没有断言? – 2009-06-25 14:15:45

+0

@约翰 - 没有什么。通过创建课程让你成为第一个绿色栏。 – 2009-06-25 14:39:56

1

单元测试必须快。如果一个测试涉及到数据库,这是一个集成测试(这也是有价值的),而不是单元测试。

至于需要审核的客户数量,我不会特别感兴趣知道我有20个需要审核,但对于特定的客户,我是否正确地决定客户是否需要审核根据我的业务规则进行审查?

您可能会发现这两个部分组成的系列 “TDD /使用与CSLA.Net Mock对象” 有所帮助:

你提到的困难依赖关系,我强烈建议与Michael Feathers的遗留代码有效地工作。本书充满conservative dependency-breaking techniques有用于带来测试代码。

1

我不熟悉C#,所以我不能帮你解决模拟问题,我想这取决于你的测试框架。

虽然我做了很多TDD,但通常我的方法是自上而下的方法。我首先想到了我想写的代码来做些什么。可以说在你的例子中,我有一个类客户端,并希望能够做一些事情,如:Client.initiate_reviews

因此,我写一个测试,设置上下文(几个客户端,一些评论)。然后调用Client.initiate_reviews,然后写出所有断言,以确定它是否已完成其工作,即对于要检查的客户端子集,现在是否有正在进行的审核,并且是否预期发送?

根据一个方法有多少副作用,在多个测试中分解它可能是谨慎的。

然后我进入客户端类并定义方法并考虑想要写入的代码。也许是这样的:

clients = Client.find_all_due_for_review 
for_each client in clients { 
    review = Review.start_new_for(client) 
    Letter.send_for_review(review) 
} 

然后我会写这个方法中调用的方法的测试,我必须实现。 find_all_due_for_review没有副作用,但返回一些东西,所以当然你会在这里测试返回值,也许没有什么变化。 并重复,直到第一次测试成功。

这样,每个方面都经过了适当的测试,您甚至可以用一些方法重新使用。

希望这会有所帮助!