ChatGPT解决这个技术问题 Extra ChatGPT

Unit testing private methods in C#

Visual Studio allows unit testing of private methods via an automatically generated accessor class. I have written a test of a private method that compiles successfully, but it fails at runtime. A fairly minimal version of the code and the test is:

//in project MyProj
class TypeA
{
    private List<TypeB> myList = new List<TypeB>();

    private class TypeB
    {
        public TypeB()
        {
        }
    }

    public TypeA()
    {
    }

    private void MyFunc()
    {
        //processing of myList that changes state of instance
    }
}    

//in project TestMyProj           
public void MyFuncTest()
{
    TypeA_Accessor target = new TypeA_Accessor();
    //following line is the one that throws exception
    target.myList.Add(new TypeA_Accessor.TypeB());
    target.MyFunc();

    //check changed state of target
}

The runtime error is:

Object of type System.Collections.Generic.List`1[MyProj.TypeA.TypeA_Accessor+TypeB]' cannot be converted to type 'System.Collections.Generic.List`1[MyProj.TypeA.TypeA+TypeB]'.

According to intellisense - and hence I guess the compiler - target is of type TypeA_Accessor. But at runtime it is of type TypeA, and hence the list add fails.

Is there any way I can stop this error? Or, perhaps more likely, what other advice do other people have (I predict maybe "don't test private methods" and "don't have unit tests manipulate the state of objects").

You need an accessor for private class TypeB. Accessor TypeA_Accessor provides access to private and protected methods of TypeA. However TypeB is not a method. It is a class.
Accessor provides access to private/protected methods, members, properties, and events. It does not provide access to private/protected classes within your class. And private/protected classes (TypeB) are intended to be used only by methods of owning class (TypeA). So basically you are trying to add private class (TypeB) from outside of TypeA to "myList" which is private. Since you are using accessor, there is no problem to access myList. However you can not use TypeB through accessor. Posiible solution would be to move TypeB outside of TypeA. But it can break your design.
Feel that testing private methods should be done by the following stackoverflow.com/questions/250692/…

p
phoenix

You can use the PrivateObject class:

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(expectedVal, retVal);

Note: PrivateObject and PrivateType are not available for projects targeting netcoreapp2.0 - GitHub Issue 366


This is the correct answer, now that Microsoft has added PrivateObject.
Good answer but please note that the PrivateMethod needs to be "protected" in stead of "private".
@HerbalMart: Perhaps I misunderstand you, but if you are suggesting that PrivateObject can only access protected members and not private ones, you are mistaken.
@JeffPearce For static methods you can use "PrivateType pt = new PrivateType(typeof(MyClass));", and then call InvokeStatic on the pt object as you would call Invoke on a private object.
In case anyone was wondering as of MSTest.TestFramework v1.2.1 - the PrivateObject and PrivateType classes are unavailable for projects targeting .NET Core 2.0 - There's a github issue for this: github.com/Microsoft/testfx/issues/366
C
Community

“There is nothing called as standard or best practice, probably they are just popular opinions”.

Same holds true for this discussion as well.

https://i.stack.imgur.com/MA56m.png

It all depends on what you think is a unit , if you think UNIT is a class then you will only hit the public method. If you think UNIT is lines of code hitting private methods will not make you feel guilty.

If you want to invoke private methods you can use "PrivateObject" class and call the invoke method. You can watch this indepth youtube video ( http://www.youtube.com/watch?v=Vq6Gcs9LrPQ ) which shows how to use "PrivateObject" and also discusses if testing of private methods are logical or not.


All the pedantic wankery in our field causes more problems than it solves. This is why one of my programming teachers said, "Make everything public!"
C
Code-Apprentice

Another thought here is to extend testing to "internal" classes/methods, giving more of a white-box sense of this testing. You can use InternalsVisibleTo attribute on the assembly to expose these to separate unit testing modules.

In combination with sealed class you can approach such encapsulation that test method are visible only from unittest assembly your methods. Consider that protected method in sealed class is de facto private.

[assembly: InternalsVisibleTo("MyCode.UnitTests")]
namespace MyCode.MyWatch
{
    #pragma warning disable CS0628 //invalid because of InternalsVisibleTo
    public sealed class MyWatch
    {
        Func<DateTime> _getNow = delegate () { return DateTime.Now; };
    

       //construktor for testing purposes where you "can change DateTime.Now"
       internal protected MyWatch(Func<DateTime> getNow)
       {
           _getNow = getNow;
       }

       public MyWatch()
       {            
       }
   }
}

And unit test:

namespace MyCode.UnitTests
{

[TestMethod]
public void TestminuteChanged()
{
    //watch for traviling in time
    DateTime baseTime = DateTime.Now;
    DateTime nowforTesting = baseTime;
    Func<DateTime> _getNowForTesting = delegate () { return nowforTesting; };

    MyWatch myWatch= new MyWatch(_getNowForTesting );
    nowforTesting = baseTime.AddMinute(1); //skip minute
    //TODO check myWatch
}

[TestMethod]
public void TestStabilityOnFebruary29()
{
    Func<DateTime> _getNowForTesting = delegate () { return new DateTime(2024, 2, 29); };
    MyWatch myWatch= new MyWatch(_getNowForTesting );
    //component does not crash in overlap year
}
}

Yes, that's what I'm suggesting. It's a little bit "hacky", but at least they're not "public".
This is a wonderful answer just because it doesn't say "don't test private methods" but yes, it's quite "hacky". I wish there was a solution. IMO it's bad to say "private methods shouldn't be tested" because the way I see it: it's equivalent to "private methods shouldn't be correct".
ya ken, I also confused by those who claim that private methods shouldn't be tested in unit test. Public API are the output, but sometimes wrong implementation also give the right output. Or the implementation made some bad side effects, e.g. holding resources that are not necessary, referencing objects preventing it from being collected by gc...etc. Unless they provide other test that can cover the private methods rather than unit test, otherwise I would consider that they can't maintain a 100% tested code.
Nice. I was not aware of this. Exactly what I needed. Let's say I build a 3rd party component for others to use, but I only want to expose a very limited API. Internally it's quite complex and I would like to test individual components (like an input parser or validator for example), but i don't want to make those public. The end-user shouldn't need to know anything about those. I know the standard approach here is "test only your public API", but I would prefer testing single responsibility units of code instead. This allows me to do it without making them public.
I think it should be the accepted answer too. Debate about private method testing is cool to read, but it's more a matter of opinion. As I see it pretty good arguments have been made on both sides. If you still want/need to test private methods, you should have a way to. This is the only one that was provided. Additionally, you can do it for a whole assembly, using : [assembly: InternalsVisibleTo("UT.cs")] in your AssemblyInfo.cs
J
Jack Davidson

One way to test private methods is through reflection. This applies to NUnit and XUnit, too:

MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);

call methods static and non static ?
The downside of reflection-reliant methods is that they tend to break when you rename methods using R#. It might not be a big problem on small projects but on huge code-bases it becomes kinda nagging to have unit tests breaking in such a fashion and then having to go around and quick-fix them. In this sense I my money goes to Jeff's answer.
@XDS Too bad nameof() doesn't work to get the name of a private method from outside its class.
C
CP70

Ermh... Came along here with exactly the same problem: Test a simple, but pivotal private method. After reading this thread, it appears to be like "I want to drill this simple hole in this simple piece of metal, and I want to make sure the quality meets the specs", and then comes "Okay, this is not to easy. First of all, there is no proper tool to do so, but you could build a gravitational-wave observatory in your garden. Read my article at http://foobar.brigther-than-einstein.org/ First, of course, you have to attend some advanced quantum physics courses, then you need tons of ultra-cool nitrogenium, and then, of course, my book available at Amazon"...

In other words...

No, first things first.

Each and every method, may it private, internal, protected, public has to be testable. There has to be a way to implement such tests without such ado as was presented here.

Why? Exactly because of the architectural mentions done so far by some contributors. Perhaps a simple reiteration of software principles may clear up some missunderstandings.

In this case, the usual suspects are: OCP, SRP, and, as always, KIS.

But wait a minute. The idea of making everything publicly available is more of less political and a kind of an attitude. But. When it comes to code, even in then Open Source Community, this is no dogma. Instead, "hiding" something is good practice to make it easier to come familiar with a certain API. You would hide, for example, the very core calculations of your new-to-market digital thermometer building block--not to hide the maths behind the real measured curve to curious code readers, but to prevent your code from becoming dependent on some, perhaps suddenly important users who could not resist using your formerly private, internal, protected code to implement their own ideas.

What am I talking about?

private double TranslateMeasurementIntoLinear(double actualMeasurement);

It's easy to proclaim the Age of Aquarius or what is is been called nowadays, but if my piece of sensor gets from 1.0 to 2.0, the implementation of Translate... might change from a simple linear equation that is easily understandable and "re-usable" for everybody, to a pretty sophisticated calculation that uses analysis or whatever, and so I would break other's code. Why? Because they didn't understand the very priciples of software coding, not even KIS.

To make this fairy tale short: We need a simple way to test private methods--without ado.

First: Happy new year everyone!

Second: Rehearse your architect lessons.

Third: The "public" modifier is religion, not a solution.


I agree completely. I'm also wondering what we should do, in case of selling a product that contains code. We have to be clear about the public API but it is great to have a bunch of tests for the internal functionality too. If everything is tested, and small, it will probably work correctly. But those little functions that nobody has to call, need to be tested too!
Pick your poison. Code with strict access right, but thin unit tests, or copious unit tests on a class with completely public methods. Not exactly a choice a professional wants to make.
R
Roger Hill

Another option that has not been mentioned is just creating the unit test class as a child of the object that you are testing. NUnit Example:

[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
    [Test]
    public void TestSomeProtectedMethod()
    {
        Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
    }
}

This would allow easy testing of private and protected (but not inherited private) methods, and it would allow you to keep all your tests separate from the real code so you aren't deploying test assemblies to production. Switching your private methods to protected methods would be acceptable in a lot of inherited objects, and it is a pretty simple change to make.

HOWEVER...

While this is an interesting approach to solving the problem of how to test hidden methods, I am unsure that I would advocate that this is the correct solution to the problem in all cases. It seems a little odd to be internally testing an object, and I suspect there might be some scenarios that this approach will blow up on you. (Immutable objects for example, might make some tests really hard).

While I mention this approach, I would suggest that this is more of a brainstormed suggestion than a legitimate solution. Take it with a grain of salt.

EDIT: I find it truly hilarious that people are voting this answer down, since I explicitly describe this as a bad idea. Does that mean that people are agreeing with me? I am so confused.....


It's a creative solution but kinda hacky.
This works really well. I've done this a few times as needed. The upside is that renaming the target class or the method under test doesn't break anything, as one would see with reflection.
K
Kai Hartmann

From the book Working Effectively with Legacy Code:

"If we need to test a private method, we should make it public. If making it public bothers us, in most cases, it means that our class is doing too much and we ought to fix it."

The way to fix it, according to the author, is by creating a new class and adding the method as public.

The author explains further:

"Good design is testable, and design that isn't testable is bad."

So, within these limits, your only real option is to make the method public, either in the current or a new class.


Secure coding practices dictate that we should make everything private unless it absolutely needs to be public, like an API interface. If every little thing in your program is somewhere defined as public then it could be called by malicious actors to use your code to attack the system. You want to reduce the attack surface of your binaries while also being testable.
@hackslash making things private doesn't hide anything and doesn't prohibit other assemblies or objects from actually calling them. It just adds compile time type checking.
This is sound advice to consider when faced with this problem, but definitely not the go-to answer. Essentially it says "never use private methods" (or alternatively: don't test all your methods). In fairy tale academia or in-house app development scenarios that might work. But forget it for a complex library with a public API. Imagine you already have a complex library with a large object graph of inter-dependent classes. Now make it WAY more difficult by moving all private code to new dummy classes and somehow share state with them.
Instead of answering the original question ("how do I test private methods?"), this resposnse instead answers the question that no one has asked ("should I test private methods?").
l
lstanczyk

I use this helper (object type extension)

 public static  TReturn CallPrivateMethod<TReturn>(
        this object instance,
        string methodName,
        params object[] parameters)
    {
        Type type = instance.GetType();
        BindingFlags bindingAttr = BindingFlags.NonPublic | BindingFlags.Instance;
        MethodInfo method = type.GetMethod(methodName, bindingAttr);

        return (TReturn)method.Invoke(instance, parameters);
    }

You can call it like this

Calculator systemUnderTest = new Calculator();
int result = systemUnderTest.CallPrivateMethod<int>("PrivateAdd",1,8);

One of the advantages is that it uses generics to pre-determine return type.


D
Darkgaze

Extract private method to another class, test on that class; read more about SRP principle (Single Responsibility Principle)

It seem that you need extract to the private method to another class; in this should be public. Instead of trying to test on the private method, you should test public method of this another class.

We has the following scenario:

Class A
+ outputFile: Stream
- _someLogic(arg1, arg2) 

We need to test the logic of _someLogic; but it seem that Class A take more role than it need(violate the SRP principle); just refactor into two classes

Class A1
    + A1(logicHandler: A2) # take A2 for handle logic
    + outputFile: Stream
Class A2
    + someLogic(arg1, arg2) 

In this way someLogic could be test on A2; in A1 just create some fake A2 then inject to constructor to test that A2 is called to the function named someLogic.


How would you test a scenario where the responsibility of the class is to produce data (which it does privately, say, by handling an event from some third party library) and then expose that data publicly in a known format, and you want to test that public interface? None of the answers here claiming we should not test methods account for producer scenarios.
@allmhuran: you should provide one more constructor which contains input event producer for example: p = ProduceData(inputEventProducer); then simulate event ocurring by calling inputEventProducer.emit("some event" ) and test the output of p. In general, if it is hard to test there are some problem with code that we need to refactor. Or if you cannot modified code you need some higher level testing like intergation or E2E.
But that constructor wouldn't be part of the normal flow of the production application. You'd basically be coding a mock into the non-mock class just for the sake of testing. This is why it makes sense to be able to call the private method as part of a test... because that's what's actually going to happen in production. That is to say, yes, the translator takes an injected dependency, but the injected dependency is from a third party library (it may be a class, not an interface).
P
Patrick Knott
public static class PrivateMethodTester
{
    public static object InvokePrivateMethodWithReturnType<T>(this T testObject, string methodName, Type[] methodParamTypes, object[] parameters)
    {
        //shows that we want the nonpublic, static, or instance methods.
        var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Instance;

        //gets the method, but we need the methodparamtypes so that we don't accidentally get an ambiguous method with different params.
        MethodInfo methodInfo = testObject.GetType().GetMethod(methodName, flags, null, methodParamTypes, null);
        if (methodInfo == null)
        {
            throw new Exception("Unable to find method.");
        }

        //invokes our method on our object with the parameters.
        var result = methodInfo.Invoke(testObject, parameters);
        if (result is Task task)
        {
            //if it is a task, it won't resolve without forcing it to resolve, which means we won't get our exceptions.
            task.GetAwaiter().GetResult();
        }

        return result;
    }
}

Call it this way:

        Type[] paramTypes = new Type[] { typeof(OrderTender), typeof(string) };
        var parameters = new object[] { orderTender, OrderErrorReasonNames.FailedToCloneTransaction };

        myClass.InvokePrivateMethodWithReturnType("myPrivateMethodName", paramTypes, parameters);

A
Allen

In VS 2005/2008 you can use private accessor to test private member,but this way was disappear in later version of VS


Good answer back in 2008 to perhaps early 2010. Now please refer to PrivateObject and Reflection alternatives (see several answers above). VS2010 had accessor bug(s), MS deprecated it in VS2012. Unless you are forced to stay in VS2010 or older (>18 years old build tooling) please save yourself time by avoiding private accessors. :-).
c
cyril.andreichuk

You can use nested classes to test private methods. For example (NUnit v3 is used):

    
    internal static class A
    {
        // ... other code

        private static Int32 Sum(Int32 a, Int32 b) => a + b;

        [TestFixture]
        private static class UnitTests
        {
            [Test]
            public static void OnePlusTwoEqualsThree()
            {
                Assert.AreEqual(3, Sum(1, 2));
            }
        }
    }

Furthermore tests related code can be moved to another file using 'partial class' feature, excluded from release builds using 'conditional compilation', etc. Advanced example:

File A.cs

    
    internal static partial class A
    {
        // ... other code

        private static Int32 Sum(Int32 a, Int32 b) => a + b;
    }

File A.UnitTests.cs


#if UNIT_TESTING
    partial class A
    {
        [TestFixture]
        private static class UnitTests
        {
            [Test]
            public static void OnePlusTwoEqualsThree()
            {
                Assert.AreEqual(3, Sum(1, 2));
            }
        }
    }
#endif


A
Amirhossein Yari

I had another approach that it works for me. because I always run my tests in debug mode so I used #if DEBUG to add public before my private method. so my private method is like this:

public class Test
{
    #if (DEBUG)
      public
    #endif
    string PrivateMehtod()
    {
      return "PrivateMehtod called";
    }
}

Sometimes you want to run your tests in release mode so that the optimized code is also tested.
Yes, this code works for Debug mode, but you can add another mode that run in release and optimize mode and use it just for test. for example change it to #if (DEBUG || OptimizeMode) and test in debug mode, for sure test in OptimizeMode at the end
This could make sense with two new solution configurations, DEBUG_TEST and DEBUG_RELEASE. Otherwise, it will behave normally.
P
PhOeNiX

Sadly there is no PrivateObject class in .net6

However I wrote a small extension method capable of invoking private methods using reflection.

Have a look at the sample code:

class Test
{
  private string GetStr(string x, int y) => $"Success! {x} {y}";
}

var test = new Test();
var res = test.Invoke<string>("GetStr", "testparam", 123);
Console.WriteLine(res); // "Success! testparam 123"

And here is the implementation of the extension method:

/// <summary>
/// Invokes a private/public method on an object. Useful for unit testing.
/// </summary>
/// <typeparam name="T">Specifies the method invocation result type.</typeparam>
/// <param name="obj">The object containing the method.</param>
/// <param name="methodName">Name of the method.</param>
/// <param name="parameters">Parameters to pass to the method.</param>
/// <returns>The result of the method invocation.</returns>
/// <exception cref="ArgumentException">When no such method exists on the object.</exception>
/// <exception cref="ArgumentException">When the method invocation resulted in an object of different type, as the type param T.</exception>
/// <example>
/// class Test
/// {
///   private string GetStr(string x, int y) => $"Success! {x} {y}";
/// }
///
/// var test = new Test();
/// var res = test.Invoke&lt;string&gt;("GetStr", "testparam", 123);
/// Console.WriteLine(res); // "Success! testparam 123"
/// </example>
public static T Invoke<T>(this object obj, string methodName, params object[] parameters)
{
  var method = obj.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  if (method == null)
  {
    throw new ArgumentException($"No private method \"{methodName}\" found in class \"{obj.GetType().Name}\"");
  }

  var res = method.Invoke(obj, parameters);
  if (res is T)
  {
    return (T)res;
  }

  throw new ArgumentException($"Bad type parameter. Type parameter is of type \"{typeof(T).Name}\", whereas method invocation result is of type \"{res.GetType().Name}\"");
}

S
Soma Mbadiwe

It's 2022 now!

...and we have .NET6

While this does not really answer the question, my preferred approach these days is to collocate code and test in the same C# project, with naming convention like <ClassName>.Tests.cs. Then I use internal access modifier instead of private.

In the project file, I have something like this:

<ItemGroup Condition="'$(Configuration)' == 'Release'">
  <Compile Remove="**\*.Tests.cs" />
</ItemGroup>

to exclude the test files in release builds. Modify as needed.

FAQ 1: But sometimes you want to also test code in Release (optimized) build.

Answer: I find it unnecessary. I trust that the compiler will do its job without messing up my intent. So far, I've had no reason to question its ability to do so.

FAQ 2: But I really want to keep the method (or class) private.

Answer: Lots of excellent solutions in this page to try out. In my experience, having access modifier set to internal is usually more than enough since the method (or class) won't be visible outside the project it's defined. Beyond that, there's nothing more to hide.