这是我的方法。它需要花费时间,因为它是4个阶段的重构测试。
我要公开的内容可能比问题示例中提供的组件更适合于复杂性更高的组件。
无论如何,该策略对于要通过接口(DAO,服务,控制器等)进行标准化的任何候选组件都是有效的。
1. 界面
让我们从MyDocumentService收集所有公共方法,并将它们放到一个接口中。例如。如果确实存在,请使用该选项,而不要设置任何新选项。
public interface DocumentService {
List<Document> getAllDocuments();
//more methods here...
}
然后,我们强制MyDocumentService实现此新接口。
到现在为止还挺好。没有重大变化,我们遵守了当前合同,behaivos保持不变。
public class MyDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//legacy code here as it is.
// with no changes ...
}
}
2. 遗留代码的单元测试
在这里,我们努力工作。设置测试套件。我们应该设置尽可能多的案例:成功案例和错误案例。最后这是为了保证结果的质量。
现在,我们将使用该接口作为要测试的合同,而不是测试MyDocumentService。
我不会详细介绍,如果我的代码看起来过于简单或不可知,请原谅我
public class DocumentServiceTestSuite {
@Mock
MyDependencyA mockDepA;
@Mock
MyDependencyB mockDepB;
//... More mocks
DocumentService service;
@Before
public void initService(){
service = MyDocumentService(mockDepA, mockDepB);
//this is purposed way to inject
//dependencies. Replace it with one you like more.
}
@Test
public void getAllDocumentsOK(){
// here I mock depA and depB
// wanted behaivors...
List<Document> result = service.getAllDocuments();
Assert.assertX(result);
Assert.assertY(result);
//... As many you think appropiate
}
}
此阶段花费的时间比任何其他方法都要长。这是最重要的,因为它将为将来的比较设定参考点。
注意:由于未进行重大更改,behaivor保持不变。我建议在这里在SCM中做一个标签。标签或分支无关紧要。只是做一个版本。
我们希望它用于回滚,版本比较,并且可能用于旧代码和新代码的并行执行。
3. 重构
重构将被实现到一个新组件中。我们不会对现有代码进行任何更改。第一步就像复制和粘贴MyDocumentService并将其重命名为CustomDocumentService一样容易。
新类继续实现DocumentService。然后去重构getAllDocuments()。(让我们开始一个。Pin重构)
可能需要对DAO的界面/方法进行一些更改。如果是这样,请勿更改现有代码。在DAO界面中实现您自己的方法。将旧代码注释为“ 已弃用”,稍后您将知道应删除的内容。
不要中断/更改现有的实现,这一点很重要。我们要并行执行两个服务,然后比较结果。
public class CustomDocumentService implements DocumentService {
@Override
public List<Document> getAllDocuments(){
//new code here ...
//due to im refactoring service
//I do the less changes possible on its dependencies (DAO).
//these changes will come later
//and they will have their own tests
}
}
4.更新DocumentServiceTestSuite
好的,现在比较简单。添加新组件的测试。
public class DocumentServiceTestSuite {
@Mock
MyDependencyA mockDepA;
@Mock
MyDependencyB mockDepB;
DocumentService service;
DocumentService customService;
@Before
public void initService(){
service = MyDocumentService(mockDepA, mockDepB);
customService = CustomDocumentService(mockDepA, mockDepB);
// this is purposed way to inject
//dependencies. Replace it with the one you like more
}
@Test
public void getAllDocumentsOK(){
// here I mock depA and depB
// wanted behaivors...
List<Document> oldResult = service.getAllDocuments();
Assert.assertX(oldResult);
Assert.assertY(oldResult);
//... As many you think appropiate
List<Document> newResult = customService.getAllDocuments();
Assert.assertX(newResult);
Assert.assertY(newResult);
//... The very same made to oldResult
//this is optional
Assert.assertEquals(oldResult,newResult);
}
}
现在,我们分别对oldResult和newResult进行了独立验证,但我们也可以相互比较。最后的验证是可选的,并且取决于结果。可能不是可比的。
用这种方式比较两个集合可能不会引起太多的注意,但是对于任何其他类型的对象(pojo,数据模型实体,DTO,包装器,本机类型...)都有效。
笔记
我不敢告诉如何进行单元测试或如何使用模拟库。我也不敢说您必须如何进行重构。我想做的是建议一项全球战略。如何进行取决于您。您确切地知道代码的方式,代码的复杂性以及这种策略是否值得一试。时间和资源等事实在这里至关重要。您将来对这些测试的期望也很重要。
我已经以服务部门为例开始了我的工作,接下来我将介绍DAO等。深入到依赖级别。或多或少可以将其描述为自下而上的策略。但是,对于较小的更改/重构(例如在巡回示例中公开的更改/重构),自下而上会使任务更容易。因为更改的范围很小。
最后,由您决定删除不推荐使用的代码并将旧的依赖项重定向到新的依赖项。
删除也不推荐使用的测试,然后完成工作。如果使用测试对旧解决方案进行了版本控制,则可以随时进行检查和比较。
由于进行了如此多的工作,您已经对旧代码进行了测试,验证和版本控制。以及经过测试,验证和准备进行版本控制的新代码。