How to unit test private methods in BDD / TDD?

前端 未结 12 959
攒了一身酷
攒了一身酷 2020-12-15 22:03

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 ques

相关标签:
12条回答
  • 2020-12-15 22:17

    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.

    0 讨论(0)
  • 2020-12-15 22:18

    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.

    0 讨论(0)
  • 2020-12-15 22:21

    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.

    0 讨论(0)
  • 2020-12-15 22:22

    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

    0 讨论(0)
  • 2020-12-15 22:25

    Mbunit Reflector helps you with this.

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

    A blog post about it.

    0 讨论(0)
  • 2020-12-15 22:27

    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
    
    0 讨论(0)
提交回复
热议问题