我正在构建一个类库,它将有一些公共和私有方法。我希望能够对私有方法进行单元测试(主要是在开发过程中,但也可以用于将来的重构)。

正确的做法是什么?


当前回答

你可以用两种方法对私有方法进行单元测试

you can create instance of PrivateObject class the syntax is as follows PrivateObject obj= new PrivateObject(PrivateClass); //now with this obj you can call the private method of PrivateCalss. obj.PrivateMethod("Parameters"); You can use reflection. PrivateClass obj = new PrivateClass(); // Class containing private obj Type t = typeof(PrivateClass); var x = t.InvokeMember("PrivateFunc", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, obj, new object[] { 5 });

其他回答

I don't agree with the "you should only be interested in testing the external interface" philosophy. It's a bit like saying that a car repair shop should only have tests to see if the wheels turn. Yes, ultimately I'm interested in the external behavior but I like my own, private, internal tests to be a bit more specific and to the point. Yes, if I refactor, I may have to change some of the tests, but unless it's a massive refactor, I'll only have to change a few and the fact that the other (unchanged) internal tests still work is a great indicator that the refactoring has been successful.

You can try to cover all internal cases using only the public interface and theoretically it's possible to test every internal method (or at least every one that matters) entirely by using the public interface but you may have to end up standing on your head to achieve this and the connection between the test cases being run through the public interface and the internal portion of the solution they're designed to test may be difficult or impossible to discern. Having pointed, individual tests that guarantee that the internal machinery is working properly is well worth the minor test changes that come about with refactoring - at least that's been my experience. If you have to make huge changes to your tests for every refactoring, then maybe this doesn't make sense, but in that case, maybe you ought to rethink your design entirely. A good design should be flexible enough to allow for most changes without massive redesigns.

在CodeProject上,有一篇文章简要讨论了测试私有方法的优缺点。然后它提供一些反射代码来访问私有方法(类似于Marcus上面提供的代码)。我在这个示例中发现的唯一问题是代码没有考虑重载方法。

你可以在这里找到文章:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx

我还使用了InternalsVisibleToAttribute方法。值得一提的是,如果你为了达到这个目的而将你之前的私有方法变成内部方法,那么也许它们不应该成为直接单元测试的对象。

毕竟,您是在测试类的行为,而不是它的具体实现——您可以在不更改前者的情况下更改后者,并且您的测试仍然应该通过。

我想在这里创建一个清晰的代码示例,您可以在任何想要测试私有方法的类上使用它。

在您的测试用例类中只包括这些方法,然后按照指示使用它们。

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$this->_callMethod('_someFunctionName', array(param1,param2,param3));

只需要按照它们在原始私有函数中出现的顺序发出参数

如果你正在使用。net,你应该使用InternalsVisibleToAttribute。