How do you unit test private methods?

前端 未结 30 1450
无人及你
无人及你 2020-11-22 06:44

I\'m building a class library that will have some public & private methods. I want to be able to unit test the private methods (mostly while developing, but also it coul

相关标签:
30条回答
  • 2020-11-22 07:42

    I tend not to use compiler directives because they clutter things up quickly. One way to mitigate it if you really need them is to put them in a partial class and have your build ignore that .cs file when making the production version.

    0 讨论(0)
  • 2020-11-22 07:43

    It might not be useful to test private methods. However, I also sometimes like to call private methods from test methods. Most of the time in order to prevent code duplication for test data generation...

    Microsoft provides two mechanisms for this:

    Accessors

    • Goto the class definition's source code
    • Right-click on the name of the class
    • Choose "Create Private Accessor"
    • Choose the project in which the accessor should be created => You will end up with a new class with the name foo_accessor. This class will be dynamically generated during compilation and privides all members public available.

    However, the mechanism is sometimes a bit intractable when it comes to changes of the interface of the original class. So, most of the times I avoid using this.

    PrivateObject class The other way is to use Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject

    // Wrap an already existing instance
    PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );
    
    // Retrieve a private field
    MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );
    
    // Call a private method
    accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );
    
    0 讨论(0)
  • 2020-11-22 07:43

    I've also used the InternalsVisibleToAttribute method. It's worth mentioning too that, if you feel uncomfortable making your previously private methods internal in order to achieve this, then maybe they should not be the subject of direct unit tests anyway.

    After all, you're testing the behaviour of your class, rather than it's specific implementation - you can change the latter without changing the former and your tests should still pass.

    0 讨论(0)
  • 2020-11-22 07:43

    Also note that the InternalsVisibleToAtrribute has a requirement that your assembly be strong named, which creates it's own set of problems if you're working in a solution that had not had that requirement before. I use the accessor to test private methods. See this question that for an example of this.

    0 讨论(0)
  • 2020-11-22 07:44

    MbUnit got a nice wrapper for this called Reflector.

    Reflector dogReflector = new Reflector(new Dog());
    dogReflector.Invoke("DreamAbout", DogDream.Food);
    

    You can also set and get values from properties

    dogReflector.GetProperty("Age");
    

    Regarding the "test private" I agree that.. in the perfect world. there is no point in doing private unit tests. But in the real world you might end up wanting to write private tests instead of refactoring code.

    0 讨论(0)
  • 2020-11-22 07:45

    Here's an example, first the method signature:

    private string[] SplitInternal()
    {
        return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                            .Cast<Match>()
                            .Select(m => m.Value)
                            .Where(s => !string.IsNullOrEmpty(s))
                            .ToArray();
    }
    

    Here's the test:

    /// <summary>
    ///A test for SplitInternal
    ///</summary>
    [TestMethod()]
    [DeploymentItem("Git XmlLib vs2008.dll")]
    public void SplitInternalTest()
    {
        string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
        object[] values = new object[] { 2, "Martin" };
        XPathString xp = new XPathString(path, values);
    
        PrivateObject param0 = new PrivateObject(xp);
        XPathString_Accessor target = new XPathString_Accessor(param0);
        string[] expected = new string[] {
            "pair[path/to/@Key={0}]",
            "Items",
            "Item[Name={1}]",
            "Date"
        };
        string[] actual;
        actual = target.SplitInternal();
        CollectionAssert.AreEqual(expected, actual);
    }
    
    0 讨论(0)
提交回复
热议问题