重构测试
我有一段代码大致相当于以下内容。
public class ConcreteThread extends OtherThread {
private DAOfirst firstDAO;
private DAOsecond secondDAO;
private TransformService transformService;
private NetworkService networkService;
public ConcreteThread(DAOfirst first, DAOsecond second, TransformService service1,
NetworkService service2) {
firstDAO = first;
secondDAO = second;
transformService = service1;
networkService = service2;
}
public Future go() {
Results r1 = firstDAO.getResults();
MyCallable c1 = new MyCallable(r1);
return super.getThreadPool().submit(c1);
}
private class MyCallable implements Callable {
private Results result;
private Long count;
private MyCallable(Results r) {
this.result = r;
this.count = new Long(0);
}
public Long call() {
Singleton transactions = Singleton.getInstance();
try {
transactions.begin();
while(result != null) {
Transformed t = transformService.transform(r1);
networkService.sendSomewhere(t);
count = count += result.size();
secondDao.persist(result);
result = firstDao.getNext(result);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
transactions.end();
}
}
}
这些类(内部类或外部类)都没有单元测试,并且事实证明内部类 MyCallable
存在错误。在我上面提供的代码的简化版本中,不存在该错误。
因此,假设您决定修复该错误,并为 MyCallable
实现一些单元测试。我的问题是这样的;您究竟会如何为 MyCallable
内部类编写单元测试?
我自己的解决方案是首先重构 MyCallable
和 ConcreteThread
。 MyCallable
在其自己的文件中成为公共类,并且 ConcreteThread
现在将 DAO、Services 和 Singleton 作为构造函数参数传递给 MyCallable
,而不是而不是依赖内部类对其私有变量的访问。
然后,我在单元测试中大量使用 EasyMock 来模拟这些依赖项并验证它们是否按照我预期的方式调用。
所有这一切的结果是 MyCallable
的代码比原来要大一些。由于它不再有权访问 ConcreteThread
中的私有变量,ConcreteThread
必须将它们作为构造函数中的参数传递,并且 MyCallable
将它们设置为私有变量。
您认为这是错误的做法吗?也许通过执行这种重构,我破坏了封装并向代码库添加了不必要的样板?您会在测试中使用反射吗?
I have a piece of code roughly equivalent to the following.
public class ConcreteThread extends OtherThread {
private DAOfirst firstDAO;
private DAOsecond secondDAO;
private TransformService transformService;
private NetworkService networkService;
public ConcreteThread(DAOfirst first, DAOsecond second, TransformService service1,
NetworkService service2) {
firstDAO = first;
secondDAO = second;
transformService = service1;
networkService = service2;
}
public Future go() {
Results r1 = firstDAO.getResults();
MyCallable c1 = new MyCallable(r1);
return super.getThreadPool().submit(c1);
}
private class MyCallable implements Callable {
private Results result;
private Long count;
private MyCallable(Results r) {
this.result = r;
this.count = new Long(0);
}
public Long call() {
Singleton transactions = Singleton.getInstance();
try {
transactions.begin();
while(result != null) {
Transformed t = transformService.transform(r1);
networkService.sendSomewhere(t);
count = count += result.size();
secondDao.persist(result);
result = firstDao.getNext(result);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
transactions.end();
}
}
}
Neither of these classes (the inner or outer) have unit tests, and it turns out that the inner class, MyCallable
, has a bug in it. In the simplified version of the code that I've given you above, the bug isn't present.
So, lets assume you decide to fix the bug, and implement some unit tests for MyCallable
. My question is this; How exactly would you go about writing unit tests for the MyCallable
inner class?
My own solution was to first refactor MyCallable
and ConcreteThread
. MyCallable
was made a public class in its own file, and ConcreteThread
now passes in DAOs, Services and the Singleton as constructor arguments to MyCallable
, rather than relying on an inner-class' access to it's private variables.
I then used EasyMock heavily in the Unit tests to mock those dependencies and verify that they were being called in the manner I expected.
A consequence of all this is that the code for MyCallable
is somewhat larger than it was. As it no longer has access to the private variables in ConcreteThread
, ConcreteThread
must pass them in as arguments in the constructor, and MyCallable
sets them as private variables.
Do you think that this was the wrong approach? That perhaps in by performing this sort of refactoring I have broken encapsulation and added unnecessary boilerplate to the code base? Would you have used reflection in the tests instead?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是一个很好的结果,MyCallable 不再依赖于 ConcreteThread 中的更改。
我认为问题和答案非常主观,但我认为您遵循了 SOLID 重构中的原则(这是一件好事)。
如果可以的话,使 MyCallable 包受保护,而不是公开:)
That's a good consequence, MyCallable is no longer dependent on changes in ConcreteThread.
I think the question and answer are quite subjective, but I think you followed the SOLID principle in the refactoring (which is a good thing).
And if you can, make MyCallable package protected, not public :)
对我来说,内部类是外部类的实现细节。
所以我提出这个问题,您可以通过为 ConcreteThread.Go() 编写失败的单元测试来演示该错误吗?一旦对内部类进行更改,应该有什么不同 - 外部可见的更改是什么?一旦你弄清楚了——你就上路了。
To me it looks like, The inner class is an implementation detail of the outer class.
So I pose this question, can you demonstrate the bug by writing a failing unit test for ConcreteThread.Go() ? What should be different once you make the change to the inner class - what would be the externally visible change ? Once you figure that out - you'll be on your way.