-1

I need to call a private method in my unit test using PrivateObject.

private void UrgentBackupButton_Click(object parameter)
{
    // Do Something...
}

Here is my unit test:

[TestMethod]
public void TestMethod1()
{
    //MainWindowViewModel main = new MainWindowViewModel();

    PrivateObject privateObj = new
    PrivateObject(typeof(MainWindowViewModel));

    privateObj.Invoke("UrgentBackupButton_Click",null);
}

It is giving me exception at privateObj.Invoke("UrgentBackupButton_Click",null):

An exception of type 'System.MissingMethodException' occurred in mscorlib.dll but was not handled in user code

Can someone please help?

Lews Therin
  • 3,707
  • 2
  • 27
  • 53
Abhi
  • 1
  • 1
  • `UrgentBackupButton_Click` seems to be an event handler (with incorrect parameters, probably due to copy/paste), unit-testing are not applicable to UI. To test UI you have to use some automated tests, which will simulate user (typing text, moving mouse, clicking, etc). Consider to refactor your code to have unit-testable model with public methods, where all stuff goes. – Sinatr May 21 '19 at 14:13
  • @Sinatr Yes , I found a solution. It was incorrect parameter. Instead of directly passing the null as a parameter i did this - > Object paramaeter = null; and privateObj.Invoke("UrgentBackupButton_Click",paramaeter). This worked!. I know it was small thing. – Abhi May 23 '19 at 14:17

2 Answers2

2

Unit tests are supposed to test the public interface of a C# class. Private methods are not accessible to other classes and therefore it makes sense for the testing class to also not have access.

UrgentBackupButton_Click seems to be an event handler so if you want to test it you have to find a way to trigger the event manually instead of invoking it but that will depend on the platform you are working on (WPF, UWP, Xamarin, etc)

dimlucas
  • 5,040
  • 7
  • 37
  • 54
  • This is the answer. One side note: `if you want to test it you have to find a way to trigger the event manually` if you raise a WinForms event, this is no longer a unit test, it is an integration test. Unit tests should have dependencies abstracted away (mocked or faked) so that the unit test can run only in memory. – Lews Therin May 21 '19 at 14:05
  • @LewsTherin Yes its a WPF platform. How to trigger the event UrgentBackupButton_Click can you tell me. Because when i just made it to public from private and then called it unit test i was able to do so. – Abhi May 22 '19 at 06:25
1

If you find yourself needing to unit test a private method, one solution is to separate that method into its own class, or if it's a pure method, perhaps into a static method that can be tested separately.

This is also helpful when you're dealing with methods called from the UI. In order to test the method through the UI you need some sort of automated integration test. But if you put that same code in a separate class, you can now test it without the UI. Now it's a unit test.

That's one reason why it's beneficial to separate as much application logic as possible from our UI. Writing a unit test that calls a method is easy. Writing an integration test that loads a form and presses a button to invoke exactly the same code is less easy.

I'm not suggesting that we should do this with every private method in every class. If our classes are small enough then we can test everything through the public interface. But by its nature a form is going to invoke all sorts of code, and it wouldn't make sense for tons of it to be in private methods in one form class.

We can move code out of the form using dependency injection, but we can also do it using something simple like this:

private void UrgentBackupButton_Click(object parameter)
{
    var doesSomething = new ClassThatDoesSomething();
    doesSomething.DoSomething();
}

If the code in the method depends on values it gets from the form or event arguments, you can pass those values to the class, either in its constructor or as method arguments. Wherever possible, pass non-UI types. For example, if the method needs the value of text box, pass that value, not the text box itself.

Once all of that is done, testing the event handler will be less important because it won't actually do anything except call another method in another class, and that method will have unit tests.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • Yes i tried this and it was working fine but i dont want to seperate the code from UI or also duplicate the code. – Abhi May 23 '19 at 14:14