How do you unit test private methods?

前端 未结 30 1448
无人及你
无人及你 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:32

    I'm surprised nobody has said this yet, but a solution I have employed is to make a static method inside the class to test itself. This gives you access to everything public and private to test with.

    Furthermore, in a scripting language (with OO abilities, like Python, Ruby and PHP), you can make the file test itself when run. Nice quick way of making sure your changes didn't break anything. This obviously makes a scalable solution to testing all your classes: just run them all. (you can also do this in other languages with a void main which always runs its tests as well).

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

    If you are using .net, you should use the InternalsVisibleToAttribute.

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

    In my opinion you should only unit test your classe's public API.

    Making a method public, in order to unit test it, breaks encapsulation exposing implementation details.

    A good public API solves an immediate goal of the client code and solves that goal completely.

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

    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.

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

    MS Test has a nice feature built in that makes private members and methods available in the project by creating a file called VSCodeGenAccessors

    [System.Diagnostics.DebuggerStepThrough()]
        [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
        internal class BaseAccessor
        {
    
            protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;
    
            protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            {
                m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
            }
    
            protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
                :
                    this(null, type)
            {
            }
    
            internal virtual object Target
            {
                get
                {
                    return m_privateObject.Target;
                }
            }
    
            public override string ToString()
            {
                return this.Target.ToString();
            }
    
            public override bool Equals(object obj)
            {
                if (typeof(BaseAccessor).IsInstanceOfType(obj))
                {
                    obj = ((BaseAccessor)(obj)).Target;
                }
                return this.Target.Equals(obj);
            }
    
            public override int GetHashCode()
            {
                return this.Target.GetHashCode();
            }
        }
    

    With classes that derive from BaseAccessor

    such as

    [System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class SomeClassAccessor : BaseAccessor
    {
    
        protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));
    
        internal SomeClassAccessor(global::Namespace.Someclass target)
            : base(target, m_privateType)
        {
        }
    
        internal static string STATIC_STRING
        {
            get
            {
                string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
                return ret;
            }
            set
            {
                m_privateType.SetStaticField("STATIC_STRING", value);
            }
        }
    
        internal int memberVar    {
            get
            {
                int ret = ((int)(m_privateObject.GetField("memberVar")));
                return ret;
            }
            set
            {
                m_privateObject.SetField("memberVar", value);
            }
        }
    
        internal int PrivateMethodName(int paramName)
        {
            object[] args = new object[] {
                paramName};
            int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                    typeof(int)}, args)));
            return ret;
        }
    
    0 讨论(0)
  • 2020-11-22 07:41

    For anyone who wants to run private methods without all the fess and mess. This works with any unit testing framework using nothing but good old Reflection.

    public class ReflectionTools
    {
        // If the class is non-static
        public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
        {
            Type t = objectUnderTest.GetType();
            return t.InvokeMember(method,
                BindingFlags.InvokeMethod |
                BindingFlags.NonPublic |
                BindingFlags.Instance |
                BindingFlags.Static,
                null,
                objectUnderTest,
                args);
        }
        // if the class is static
        public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
        {
            MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
            foreach(var member in members)
            {
                if (member.Name == method)
                {
                    return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
                }
            }
            return null;
        }
    }
    

    Then in your actual tests, you can do something like this:

    Assert.AreEqual( 
      ReflectionTools.InvokePrivate(
        typeof(StaticClassOfMethod), 
        "PrivateMethod"), 
      "Expected Result");
    
    Assert.AreEqual( 
      ReflectionTools.InvokePrivate(
        new ClassOfMethod(), 
        "PrivateMethod"), 
      "Expected Result");
    
    0 讨论(0)
提交回复
热议问题