简体   繁体   English

如何设计可用于模拟的私有/最终方法?

[英]How to design a private/final method available for mocking?

This is the class that I have to test: 这是我必须测试的类:

public class Downloader {
  public String download(String uri) {
    HttpClient client = this.getHttpClient();
    client.setURI(uri);
    return client.get();
  }
  private HttpClient getHttpClient() {
    HttpClient client = new HttpClient();
    // + some config
    return client;
  }
}

Very simple. 很简单。 Now I want to test its behavior when getHttpClient() throws an exception. 现在我想在getHttpClient()抛出异常时测试它的行为。 However, I can't mock this method, since it is private . 但是,我无法模仿这种方法,因为它是private What is a common practice in such a situation? 这种情况下的常见做法是什么?

I would make the HTTPClient a field of the class that is set up on construction (via a interface). 我会使HTTPClient成为构建时(通过接口)设置的类的字段。 Then you have the ability to create a mock HTTPClient that can throw an exception during the test if you want, eg: 然后,您可以创建一个模拟HTTPClient,如果您愿意,可以在测试期间抛出异常,例如:

public class Downloader {
  private IHTTPClient client;

  public Downloader(IHTTPClient client) {
    this.client = client;
  }

  public String download(String uri) { 
    this.initialiseHttpClient(); 
    client.setURI(uri); 
    return client.get(); 
  } 

  private HttpClient initialiseHttpClient() { 
    // + some config 
  } 
}

Then call the constructor with a real HTTPClient in production code and a Mock in the test code. 然后使用生产代码中的真实HTTPClient和测试代码中的Mock调用构造函数。 You may need to create a wrapper for HTTPClient for the real code. 您可能需要为实际代码创建HTTPClient的包装器。

If you're trying to test private methods, I think something's not quite right. 如果您正在尝试测试私有方法,我认为有些不太对劲。

You should be testing your class against its contract . 你应该根据合同测试你的课程。 The private methods are implementation-dependent, and so (in a sense) it doesn't matter what they do. 私有方法依赖于实现,因此(从某种意义上说)它们的作用并不重要。 You should be checking that your public methods work as expected in both functioning and non-functioning scenarios, and reflect this as appropriate back to the client (in this case, your test class). 您应该检查您的公共方法在功能和非功能方案中是否按预期工作,并将其反映为客户端(在本例中为您的测试类)。

You may need to substitute some functionality into your class for test purposes (eg substitute in a broken JDBC connection etc.) In that scenario I would investigate mocking and dependency injection. 可能需要将某些功能替换为类以用于测试目的(例如,在破坏的JDBC连接中替换等)。在该场景中,我将研究模拟和依赖注入。

It does sound a little cheesy but I generally make methods like this public and add conspicuous javadocs saying "this method is exposed public only for testing". 它确实听起来有点俗气,但我通常会像这样公开制作方法并添加明显的javadocs说“这种方法仅公开用于测试”。

You can also use package-only access by having the xunit/mock etc. in the same package. 您还可以通过在同一个包中使用xunit / mock等来使用仅包访问。

I tend to prefer using simple solutions like this as opposed to more complex and hard-to-debug techniques like AOP-style code injection. 我倾向于使用像这样的简单解决方案,而不是像AOP风格的代码注入那样更复杂和难以调试的技术。

You could make getHttpClient() protected and subclass it in the test to return what you want, so you'd have something like this in your tests: 您可以使getHttpClient()受到保护并在测试中将其子类化以返回您想要的内容,因此您在测试中会有类似的内容:

public class TestableDownloader extends Downloader {

  protected HttpClient getHttpClient() {
    throw new Exception();
  }
}

This isn't ideal though, you'd be better having a different design which didn't require you to test private methods (perhaps using dependency injection to provide a factory or something). 这不是理想的,你最好有一个不同的设计,不要求你测试私有方法(可能使用依赖注入来提供工厂或其他东西)。

Private methods are not supposed to get a unit test. 私有方法不应该进行单元测试。 You are only supposed to unit test public methods. 您只应该对公共方法进行单元测试。 How a public method is organized internally does not matter to unit testing. 如何在内部组织公共方法与单元测试无关。 A unit is not equal to a method. 单位不等于方法。 It is equal to a behavior that is possibly using more than one method to do its job. 它等于可能使用多种方法完成其工作的行为。

Mocking is also a useless thing to do. 模拟也是一件无用的事情。 If you have to mock something, your method is really integrating functions. 如果你必须模拟一些东西,你的方法实际上是集成函数。 Your code is needing refactoring to make it only do one thing and then a wrapper method calls it and the to be mocked object to integrate it. 你的代码需要重构才能使它只做一件事,然后一个包装器方法调用它和被模拟的对象来集成它。

Unit testing is something that sounds like you should do but in reality is a waste of effort that you are better to use in coding your application. 单元测试听起来像你应该做的事情,但实际上是浪费你最好在编写应用程序时使用。 Unit testing is no guarantee of better code quality and maybe it is making it worse because you are not spending enough time on your real code. 单元测试不能保证更好的代码质量,也许它会使它变得更糟,因为你没有在你的真实代码上花费足够的时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM