How to unit test private methods in BDD / TDD?

烂漫一生 提交于 2019-12-18 03:12:15

问题


I am trying to program according to Behavior Driven Development, which states that no line of code should be written without writing failing unit test first.

My question is, how to use BDD with private methods?
How can I unit test private methods?
Is there better solution than:
- making private methods public first and then making them private when I write public method that uses those private methods;
or
- in C# making all private methods internal and using InternalsVisibleTo attribute.


回答1:


When you write code test-first, you write against the public interface. There are no private methods at this point.

Then you write the code to pass the test. If any of that code gets factored into a private method, that's not important -- it should still be there only because it is used by the public interface.

If the code isn't written test first, then -- in .net, anyway -- reflection can be used to directly prod private methods; though this is a technique of last resort.




回答2:


Private methods are internal implementation details. They should not be tested directly, as they will be tested indirectly via testing your public interface. If for some reason a private method is not covered when your public interface is fully tested, then the private method is not required, and it should be removed.

Generally, it is a bad idea to bind test code to private implementation details. That couples your test to those private details, reducing your freedom to change those details at will, even if they don't affect the publicly facing interface and behavior. That increases the amount of effort required to write and maintain your unit tests, which is a negative thing. You should strive for as much coverage as possible, while only binding to the public interface.




回答3:


Short answer: You don't test private methods.

If you have programmed well, the code coverage of your tests should be testing private methods implicitly.




回答4:


If a private method method exists, it's there to be used by a public method. Therefore I'd write a test for the public method.

I write my tests to test the public parts of a class. If the class is well designed then the private parts get tested by default.

If the private method isn't called from a public method, then why does it exist?

In your case I'd do the following

* Write failing test for the public method
* Write public method that calls the private method that doesn't exist yet(test still fails as your class is incomplete
* Write the private method
* Test should now pass



回答5:


Short answer: You can't test a private method.

Long answer: You can't test a private method, but if you're inclined to test whatever it does consider refactoring your code. There are two trivial approaches:

  • Test the public method that accesses the private method.
  • Extract the private code to its own class, i.e. move the implementation so it can become appropriately public.

The first one is simple but has a tendency to let you shoot your own foot as you write more tests and the latter promotes better code and test design.

Contrived answer: Okay, so I lied. You can test a private method with the help of some reflection magic (some TDD tools support testing private methods). In my experience though, it leads to convoluted unit tests. Convoluted unit tests leads to worse code. Worse code leads to anger. Anger leads to hate. Hate leads to suffering

The direct effect of production code becoming worse is that the class under test tend to become large and handles many things (violation of Single Responsibility Principle) and harder to maintain. This defeats the purpose of TDD, that is to get production code testable, extensible and more importantly: reusable.

If you're writing tests for a class that is deployed, you could investigate everything that calls the private method and write tests accordingly. If you have the chance to rewrite the class then please do refactor it by splitting the class up. If you're lucky then you'll end up with some code reuse that you can utilize.




回答6:


You should only be testing the external API of your classes, i.e. the public methods. If your tests aren't hitting code in the private methods then either you need to write more tests or refactor the class.

The whole point of testing an API, especially one that will be distributed to third parties, is that you can change the internal structure of the class as much as you want, as long as you don't break the external contract of it's public methods.

As you've identified, this is where BDD comes into play over 'traditional' TDD using mock classes, where every method call has to be set-up in advance for the test. I'm not an expert on either of these, hopefully someone else can answer that one better than I can.




回答7:


I agree with the point that has been made about not testing private methods per se and that tests should be written against the public API, but there is another option you haven't listed above.

You could make the methods protected then derive from the class under test. You can expose the base protected method with a public method on the derived class, for example,

public class TestableClassToTest : ClassToTest
{
    public new void MethodToTest() 
    { 
        base.MethodToTest(); 
    } 
}

You might be using this Extract and Override pattern already to override virtual properties of the base class for dependency injection, in which case this may be a viable option for you.




回答8:


Mbunit Reflector helps you with this.

Reflector objectReflection = new Reflector(new ObjectWithprivateMethods());
objectReflection.InvokeMethod(AccessModifier.NonPublic,,"Add",1,6));

A blog post about it.




回答9:


We can Unit Test static and instance private methods using PrivateType and PrivateObject respectively. The following 2 articles explains these techniques

1. Unit Test Private Static Method in C#.NET

2. Unit Test Private Instance Method in C#.NET




回答10:


If you find yourself wanting to test a private method then there is something complex in it and you are probably right to want to test it, this is a design smell. Exposing the method on the interface just swaps one smell for another worse one.

Time to refactor :)

Usually I factor out the inner complexity into a helper class. However check the method for 'Feature Envy' or 'Inappropriate Intimacy'. There may be a better place for the method to live. With Extension methods in .net now, even base types could be a good candidate.

Good Luck




回答11:


If you really believe that a private method is complex enough that it deserves unit tests of it's own - it's an indicator that your class is doing too much and you should extract part or all of that private method into a class of its own behind an interface.

Mock the interface when testing the original class. You should now have a public accessor to the new class which was previously the private method.

Sometimes when dealing with old code that was either poorly written or not written using TDD there may be a need to test the private classes. In this case you should use reflection, but where possible update the code to follow closer to the TDD approach.




回答12:


I've been fighting with it for over 1 month, but found the answer:

        var objectOfPrivateMethod = new ObjectOfPrivateMethod(); //yes here is contructor
        object[] arguments = {  }; // here as Object you provide arguments

        var extractedPrivateMethod = typeof(ObjectOfPrivateMethod).GetMethod("Name_Of_Private_Method", BindingFlags.NonPublic|BindingFlags.Static); //if fails returns null. delete flag static if it's not static. Returns your method as an object.
        Assert.AreNotEqual(null, extractedPrivateMethod, "Mathod does not exist"); // good to catch if even exists.

        object result = extractedPrivateMethod.Invoke(null, arguments); // here as object you'll get return value of your function. change null for object of class where is method, if your method is not static 

that's all.



来源:https://stackoverflow.com/questions/1583363/how-to-unit-test-private-methods-in-bdd-tdd

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!