92

I come from the Java EE world but now I'm working on a .Net project. In Java when I wanted to test a protected method it was quite easy, just having the test class with the same package name was enough.

Is there anything similar for C#? Is there any good practice for unit testing the protected methods? I only found frameworks and people saying that I should test only public methods.

It should be possible to do it without any framework…

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
fiso
  • 1,375
  • 1
  • 14
  • 20

9 Answers9

113

You can inherit the class you are testing on your test class.

[TestClass]
public class Test1 : SomeClass
{
    [TestMethod]
    public void MyTest
    {
        Assert.AreEqual(1, ProtectedMethod());
    }

}
Danghor
  • 17
  • 2
  • 7
unarity
  • 2,339
  • 2
  • 21
  • 17
  • 2
    Thank you for your fast answer. I tested and it's working. I don't know if this will be a nice way to do it, but at least I can start with the unit testing. – fiso Nov 16 '12 at 12:26
  • 3
    @unarity What if my tested class doesn't have no args constructor (e.g. it has only 4 args constructor)? When I derive from it, my test class won't compile with `TestClass does not contain a constructor that takes 0 arguments` error – dragan.stepanovic Aug 12 '14 at 15:21
  • 4
    I add a 'Testable' class inside my Unit Test Project the extends classes and wraps all their protected methods with 'TestableMethods' and re-implements matching constructors if necessary. Same result but I like to separate the tests from the implementations. – Élie Oct 18 '15 at 23:01
  • I think this may actually be `Asert.AreEqual(1, Test1.ProtectedMethod());` since the derived type needs to be called internally. The base method is still protected despite being inherited from. – edwardrbaker Feb 12 '18 at 20:10
  • 14
    **This is wrong answer** test class should not be the same as tested class ... there are many case where tested class needs special initialization (fx it is valid only in some container) and this solution will cause problem – Selvin Oct 14 '21 at 12:53
  • 1
    It doesn't seem like a good option in a dependency injection scenario, or really any scenario where you are passing mocked dependencies into the class you're testing. – Phil B Dec 07 '21 at 18:16
46

You can expose the protected methods in a new class that inherits the class you want to test.

public class ExposedClassToTest : ClassToTest
{
    public bool ExposedProtectedMethod(int parameter)
    {
        return base.ProtectedMethod(parameter);
    }
}
typhon04
  • 2,350
  • 25
  • 22
46

Another option is to use internal for these methods and then use InternalsVisibleTo to allow your test assembly to access these methods. This does not stop the methods being consumed by other classes in the same assembly, but it does stop them being accessed by other assembles that are not your test assembly.

This does not give you as much encapsulation and protection but it's pretty straight forward and can be useful.

Add to AssemblyInfo.cs in the assembly containing the internal methods

[assembly: InternalsVisibleTo("TestsAssembly")]
Richard Garside
  • 87,839
  • 11
  • 80
  • 93
  • 8
    `protected` methods are visible to (and overridable by) subclasses (including those outside the assembly), while `internal` are not. They are not functionally equivalent and thus this option is only applicable to situations where you don't need a truly `protected` method. – E-Riz Oct 14 '19 at 19:35
  • 2
    @E-Riz if you want your internal methods to be overridable by subclasses outside the assembly you can use protected internal https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/protected-internal (neither protected internal, nor internal are equivalent to protected, but they give you different options when it comes to testing). The top answer using subclasses is probably the best solution for most cases. – Richard Garside Oct 15 '19 at 08:29
8

You can use PrivateObject class to access all the private/ protected methods/ fields.

PrivateObject is a class in the Microsoft unit testing framework which is a wrapper that enables calling normally inaccessible members for unit testing.

Cameron
  • 2,903
  • 1
  • 30
  • 31
Usama Aslam
  • 437
  • 7
  • 18
  • 20
    This answer would benefit a lot if it explained what a PrivateObject is. – Emond Mar 06 '17 at 07:00
  • 1
    It is pretty simple, just go through the documentation https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.privateobject.aspx – Usama Aslam Mar 06 '17 at 07:09
  • 20
    **Note:** This is only applicable if you're using MSTest - not for NUnit and XUnit. – Matt Mar 26 '19 at 15:06
  • Example of unit testing with private object https://stackoverflow.com/a/15607491/6901318 This works with private and protected methods – LazyDog Oct 02 '20 at 19:01
6

You can use reflection to invoke private and protected methods.

See here for more:

http://msdn.microsoft.com/en-us/library/66btctbe.aspx

Justin Harvey
  • 14,446
  • 2
  • 27
  • 30
  • 16
    Maybe I'm wrong, but I feel that if reflection is needed for testing is because I'm doing something wrong. But if in C# this is quite normal, then I will get used to. – fiso Nov 16 '12 at 12:38
  • I too would prefer not to have to use it. I really just wanted to point out this technique in case you weren't aware. It seems possible, to me at least, that you might have a private method that you want to test in isolation, to be able to easily test it with a large range of inputs and expected results. – Justin Harvey Nov 16 '12 at 12:53
  • I consider using reflection a reasonable way to test non-public features of a class. I do it all the time in Java because otherwise you have to use an "incorrect" modifier just so you can test a properly constructed class. If you need Private or Protected you should still be able to test without adding junk code to real classes to allow access (and which could be used to cause your code to fail from a security standpoint). – millebi Sep 08 '16 at 21:04
  • 1
    Reflection, regardless of how it feels, is semantically correct, because you can retain the visibility of the method and you don't have to encapsulate a method within a method. – Professor of programming Sep 24 '21 at 11:59
6

Although the accepted answer is the best one, it didn't solve my problem. Deriving from the protected class polluted my test class with a lot of other stuff. In the end I chose to extract the to-be-tested-logic into a public class and test that. Surely enough this will not work for everyone and might require quite some refactoring, but if you scrolled all the way up to this answer, it might just help you out. :) Here's an example

Old situation:

protected class ProtectedClass{
   protected void ProtectedMethod(){
      //logic you wanted to test but can't :(
   }
}

New situation:

protected class ProtectedClass{
   private INewPublicClass _newPublicClass;

   public ProtectedClass(INewPublicClass newPublicClass) {
      _newPublicClass = newPublicClass;
   }

   protected void ProtectedMethod(){
      //the logic you wanted to test has been moved to another class
      _newPublicClass.DoStuff();
   }
}

public class NewPublicClass : INewPublicClass
{
   public void DoStuff() {
      //this logic can be tested!
   }
}

public class NewPublicClassTest
{
    NewPublicClass _target;
    public void DoStuff_WithoutInput_ShouldSucceed() {
        //Arrange test and call the method with the logic you want to test
        _target.DoStuff();
    }
}
nozem
  • 467
  • 8
  • 10
3

You can create a stub with a public method that calls the protected method from the base class. This is also how you would use this protected method in production.

public class FooStub : Bar 
{
    public string MyMethodFoo()
    {
        return MyMethodBar();
    }
}

public abstract class Bar 
{
    protected string MyMethodBar()
    {
        return "Hello World!"
    }
}
BartKrul
  • 577
  • 1
  • 8
  • 21
0

Here is the extension method (what is the extension method?) I am using in my testing project. The only downside is that you have to literally write the name as a string because nameof() follows the protection rules and does not allow you to refer to protected or private members.

        public static MethodInfo GetNonPublicMethod(this Type type, string method)
        {
            MemberInfo[] temp = type.GetMember(method, MemberTypes.Method, BindingFlags.NonPublic | BindingFlags.Instance);
            if (temp.Length == 1)
            {
                if (temp[0] is MethodInfo ret)
                {
                    return ret;
                }
                else
                {
                    throw new ArgumentException("Not a method.");
                }
            }
            else
            {
                if (temp.Length == 0)
                {
                    throw new ArgumentException("Method was not found.");
                }
                else
                {
                    throw new ArgumentException("Multiple methods found.");
                }
            }
        }

More on this method here: https://learn.microsoft.com/en-us/dotnet/api/system.type.getmember?view=net-5.0

PS: Use methodInfo.Invoke(instance, params) to call it.

Daniel B
  • 11
  • 3
  • There is already an answer suggesting to use the reflection and as the comments to this answer this is not the way to go to keep code clean. – Jerome2606 Sep 15 '21 at 12:04
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-ask). – Community Sep 15 '21 at 12:04
  • 1. This is a working example for getting a protected method in the testing environment, though there is a recommendation to use it, there is no copy-pastable example. 2. This code is to be used only in the testing environment - where, generally speaking, it is more acceptable to break the conventions in order to test things without introducing any testing code to the original codebase. I admit that code can be improved, but this code does *exactly* what was asked for. – Daniel B Sep 16 '21 at 09:06
0

Approach given in the first answer doesn't work and all protected methods are hidden. The base() should be added then protected methods will be visible.

[TestClass]
public class Test1 : SomeClass
{
    public Test1() : base()
    {
    }

    [TestMethod]
    public void MyTest
    {
        Assert.AreEqual(1, ProtectedMethod());
    }
}
user3806621
  • 278
  • 1
  • 6
  • 1
    Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? **If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient.** Can you kindly [edit] your answer to offer an explanation? – Jeremy Caney Aug 30 '23 at 00:15