重构测试

发布于 2024-10-17 22:23:43 字数 2108 浏览 7 评论 0原文

我有一段代码大致相当于以下内容。


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 内部类编写单元测试?

我自己的解决方案是首先重构 MyCallableConcreteThreadMyCallable 在其自己的文件中成为公共类,并且 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烂人 2024-10-24 22:23:43

所有这一切的结果是 MyCallable 的代码比原来要大一些。由于它不再能够访问 ConcreteThread 中的私有变量,因此 ConcreteThread 必须将它们作为构造函数中的参数传递,而 MyCallable 将它们设置为私有变量。

这是一个很好的结果,MyCallable 不再依赖于 ConcreteThread 中的更改。

我认为问题和答案非常主观,但我认为您遵循了 SOLID 重构中的原则(这是一件好事)。

如果可以的话,使 MyCallable 包受保护,而不是公开:)

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.

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 :)

只有影子陪我不离不弃 2024-10-24 22:23:43

对我来说,内部类是外部类的实现细节。

所以我提出这个问题,您可以通过为 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文