-5

I have discovered a fantastic way to unit test private methods.

This is great except I don't like how the method name is typed in as a string. Is there a way to create a "safety net?" I want to type the method name so that the compiler can throw a compiler time error if the method does not exist on an object.

Private method:

public class BankAccount
{
    //Private method to test
    private bool VerifyAmount(double amount)
    {
        return (amount <= 1000);
    }
}

Unit Test:

[TestMethod()]        
public void VerifyAmountTest()
{
    //Using PrivateObject class
    PrivateObject privateHelperObject = new PrivateObject(typeof(BankAccount));                             
    double amount = 500F;
    bool expected = true;
    bool actual;
    actual = (bool)privateHelperObject.Invoke("VerifyAmount", amount);            
    Assert.AreEqual(expected, actual);            
}

I know that some people think that we should not unit test private methods. That is not the purpose of this question so let's not discuss that question and stay on topic.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Maderas
  • 231
  • 2
  • 14

2 Answers2

0

When you unit-test a class, you are essentially putting your consumer hat and calling the exposed methods of the class to verify that the class does what it claims to do.

For instance, consider this example using your BankAccount class:

public class BankAccount
 {
     public Widthdrawal WithdrawMoney(double amount)
     {
          if(!VerifyAmount(amount))
               throw new InvalidOperationException("Minimum dispensed is $1,000!");
          //Do some stuff here
          return new Withdrawal(1000);
     }
     private bool VerifyAmount(double amount)
     {
         return (amount <= 1000);
     }

 }

You can then test for a few things. For instance:

  1. That a valid amount results in a withdrawal.
  2. That an invalid amount results in an invalid operation exception.

Your tests:

[TestMethod]
public void Verify_Valid_Amount_Results_In_Widtdrawal()
{
     var bankAccount = new BankAccount();
     var withdrawal = bankAccount.WithdrawMoney(1200);
     Assert.IsNotNull(withdrawal);
     Assert.AreEqual(1200, withdrawal);
}


[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Verify_Valid_Amount_Results_In_Exception()
{
     var bankAccount = new BankAccount();
     var withdrawal = bankAccount.WithdrawMoney(800);
}

As you can see, you test the funcionality that uses the private method, not the private method itself.

If it's important for you to verify that method, you can either make it public or abstract the concept of amount verification to another class that exposes this method and can be unit tested separately.

JuanR
  • 7,405
  • 1
  • 19
  • 30
  • 1
    There are plenty of different view points on what we should unit test. You did not answer my question. You are off topic. – Maderas Sep 26 '17 at 13:36
  • 1
    It's a common misconception to think that _unit testing_ == _testing the public interface of a class_. As the name itself says, it's "testing of a _unit_", where _unit_ is a _unit of funcionality_. It's perfectly legit to test private/internal methods. In some languages like C# or Java it's more difficult (you have to use Reflection and hardcoded strings, it's tricky and boring), in other languages (Python) you don't have private methods at all, you consider "private" all the methods with an underscore, but it's just a convention and you can access and test them just as the public ones. – Massimiliano Kraus Sep 26 '17 at 13:46
  • @MassimilianoKraus: Let's not turn this into a philosophical war. This will be my last comment. If you want to test the method itself, you can always make it public or abstract the concept of the validation of amounts to another class that you can then pass to the original object. That way you can validate the amount logic separately. You may see Python's lack of access control as a feature. I consider it a miss. Anyways, to each his own. :-) – JuanR Sep 26 '17 at 13:50
  • @Juan: "philosophical"? Those ideas affects the way you write code, and the way you write tests. It's the way you _design_ your app. This is quite a _practical_ war. War? No, it's not a war, it's a simple discussion, we are all here to learn and listen from each others :-) – Massimiliano Kraus Sep 26 '17 at 14:05
  • I'm all for discussion @MassimilianoKraus but this is not the place. :-) – JuanR Sep 27 '17 at 13:14
-2

Am I right that you want check presence of private method on .Net object?

Then pick one of the following cases to extract any method from instance:

Case 1 If you don't care about method signature:

var typeOfObj = typeof(BancAccount)
               .GetMethods(
                 BindingFlags.NonPublic | 
                 BindingFlags.Instance)
               .Any( method => method.Name == testedName )

Case 2 If you need specify exact signature then use - typeof(BancAccount).GetMethod(testedName, <types of arguments>)

Dewfy
  • 23,277
  • 13
  • 73
  • 121