粗心和学习

Sep 08, 2010

    下午快下班时结果CruiseControll上显示Installer的测试没有通过,这一看原来是我提交的东东出现了问题,但是我并没有改Installer的东西,只是另一个项目做了一些修改,看起来关系比较奇怪,看起来好像没有关联。但是无可否认问题是出现我提交的那个changeset上面,然后张姐在去产看问题的时候发现其中一个领域对象居然有引用到测试的一个帮助类,然后Installer是不会包含测试项目的引用的,ah......~~~~  超尴尬,我仔细一看确实啊,这个看起来不可思议啊,我想了想当时确实有好像在测试有多处地方需要实例后,然后我说好,那我将这些东东给塞到一个测试帮助类中,这样原来测试看起来会相对明了点,下面抽取之前的代码:
[Test]
public void should_contain_remove_from_my_favorite_link_if_current_curve_has_favorites()
{
presenter = new CurveActionPanelPresenter(formulaVersion, UserMother.Trader(), true,true);
var actions = presenter.RightSideActions(htmlHelper);
Assert.IsTrue(CurveActionPanelPresenterTestHelper.Contains(actions, CurveActionPanelPresenter.REMOVE_FROM_FAVORITES));
}
    下面是CurveActionPanelPresenter的构造函数声明:
public CurveActionPanelPresenter(FormulaVersion version, IUser user, bool hasMDEPermission, bool hasFavorites)
{
this.version = version;
this.user = user;
this.hasMDEPermission = hasMDEPermission;
this.hasFavorites = hasFavorites;
}
    实际上在测试中我们在构造这个presenter的测试数据的时候,我们只关心说FormulaVersion和hasFavorites这两个参数,其他的是我们所关心的。 所以这里你可以说我可以在里面写一个构造函数,只接受这个参数不就可以了吗?  答案是:不够直白,更重要的是你测试的所表达的或者说所处的层次过于细节,我在前面写过文章“Why it matters ?” 中提到了测试的所在抽象层次,你迫使用户去关注测试数据的准备细节,而这个细节不是他应该关心的,或者说你迫使用户从测试底层(细节)来推导你想表达的意思(intention--这里实例化其实是想说准备了这样一个Curve它没有Favorites),这样一个思维角度是不必要的,也是很烦人的,如果我需要去看它的构造函数怎么实现来推导说这个初始化是要干些什么工作,这是不太好的。 鉴于这个实例化测试数据有在多个测试中有用到,所以我有想到说将它抽到一个测试的帮助类中,结果就有了以下代码:
[Test]
public void should_contain_remove_from_my_favorite_link_if_has_favorites()
{
presenter = PresenterUtil.CreateActionPanelWithoutCorrespondingFavoriteAttached(version);
var actions = presenter.RightSideActions(htmlHelper);
Assert.IsTrue(CurveActionPanelPresenterTestHelper.Contains(actions, CurveActionPanelPresenter.REMOVE_FROM_FAVORITES));
}
    一开始看上去来正常的,但是问题在于我在PresenterUtil中居然有引用到了初始化用户的测试帮助类(也就是构造User这样一个帮助类),而且这个CreateActionPanelWithoutCorrespondingFavoriteAttached也不应该是在PresenterUtil中啊,原来问题是我当时准备移动这个方法时,心中想的是这样一个***PresenterHelper的类,但是鬼使神差输成了PresenterUtil,oh my lady Gaga ! 而且问题的诡异性在于你如果不运用Installer测试它是不会报错的,可以想象如果没有自动化测试,那么以后修改这个bug将会有多么棘手。     但是问题并没有就此结束,随即而来的问题出现了,因为我当时修改FormulaList 这样一个枚举出所有Formula的页面给抽离出来成多个,因为原来是很多功能都有公用这一个显示Formula的逻辑,但是这些显示都会有根据Formula 的状态来决定显示内容,所以你可以看到在在下面这个公共的枚举页面中有很多这样或那样的判断if else,当然这些都不是很麻烦,但是当我们说有做压力测试,比如枚举出测试有1000条Formula的时间,时间会有很慢,但是如果我们将这些if /else去掉后,它的速度会提高很多,也就是说我们知道它状态就可以去掉这些跟状态相关的逻辑,将这样一个twisted显示功能页面抽离出来,就显得很有价值。所以你可以看到DRY也是说有它一定的上下文的,有时候的不是说看到代码重复就一定要怎么怎么样,这都是必须根据一定的上下文才能进行的,如果仅仅是盲目是去执行,这个时候best practice 就是会有起到反作用。      努力加强这种直觉吧,在实践中尽记。     重新回到正题,这个问题是我想把FormulaList这个文件先保留在那里,然后去创建其他特定状态需要的XXXlist文件(这种方式应该是比较谨慎的),然后跑测试都有通过,几个跟枚举Formula的页面也没有问题,我觉得有说那现在我可以很安全的删掉这个文件吧,然后我就在VS中删掉了,然后去命令行中hg addremove 一下,一把提交,当时当然觉得很ok啊,应该没有问题的啊。结果是在Installer中,这个FormulaList页面标记为缺失的记号,并不是完全没有,一看原来是项目描述中还保留着对FormulaList文件的记录,这个尴尬啊,原来我当时删除文件之后,并没有进行过编译或者其他什么的,问题是它就不自动给你检测说你有删除文件,而且是从IDE中有删除,那应该是自己很清楚的行为,那我就将项目描述文件更新。 这个如果有在Lunix下估计可能会好点,Windows下没有文件的这种Notification的机制,导致文件的操作很多都是被动的,比如说poll,你不是去poll,就不清楚文件的状态。这个时候如果我没有显示的说告诉VS去重新编译或者什么的,那它就会呆呆在那里看《大长今》,完全搞不清楚状况嘛。 所以今天出现这两个问题,我完全有说付全部责任。第一个问题自己太过粗心;第二个是自己的知识不到位。一个是习惯问题,一个是学习问题,严格要求,努力学习,才可以去知道说它原来在看《大长今》。