2

I have a class with a private function that dozens of other public functions use.

This private function just wrap other private functions that call to third party with try-catch

private T TryCatchCallWrapper<T>(Func<T> method)
    {
        try
        {
            Login();
            return method();
        }
        catch (ConnectionException e)
        {
            switch (e.ConnectionReason)
            {
                case ConnectionReasonEnum.Timeout:
                    throw new ...
            }
        }
        catch (XException e)
        {
            throw ...
        }
    }

I want to test this wrap function to be sure it catches all exceptions correctly, but I don't like to test it again for each public function that use it. (however, i want to verify they call to this function (like mock.Verify())

What is the best practice to deal with that?

One option is to extract the wrapped function to another class, but I am not sure it's good solution since the wrapped function usages and relevance are just in the current class. thanks!

arielorvits
  • 5,235
  • 8
  • 36
  • 61
  • 2
    Possible duplicate of [How do you unit test private methods?](http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods) – MakePeaceGreatAgain Feb 09 '16 at 07:30

4 Answers4

2

I think problem is in design. You should test only public functionlity directly and private should be tested indirectly. It is hard for me to say what is wrong using one wrapper function. But I want to link this question, maybe it will help to refactore your part of code. If you need mock.Verify() so use it:) Extract functionality to special class.

Community
  • 1
  • 1
NikitaKo
  • 300
  • 2
  • 12
1

I think that the best option is to change it to be protected and to have an inherited class with a wrapper function to this function. this way the only change on your production code is the private function changed to protected and the new class can be part of your test project.

silver
  • 1,633
  • 1
  • 20
  • 32
  • i edited my question and added that i want to verify the public function call to this wrap function. i dont think your solution provide this option. thanks – arielorvits Feb 09 '16 at 08:15
  • of course it does, your test is the the public function and you can call this function with any input that you want so you can verify all the flows... – silver Feb 09 '16 at 08:25
  • no, this is exactly what i want to avoid. as i mentioned "I don't like to test it again for each public function that use it (however, i want to verify they call to this function (like mock.Verify())" – arielorvits Feb 09 '16 at 08:33
  • I'm not following, if you want to test that your private function then what I suggested should work. if you want to test that your public functions call this private function then you need to move it into an interface in order to mock it and verify that it's being called. – silver Feb 09 '16 at 09:40
  • let say public T Xmethod() call TryCatchCallWrapper(). now i'm testing Xmethod. I want to be sure it call TryCatchCallWrapper(), but i dont want to test TryCatchCallWrapper internall details. so, if i'll extract TryCatchCallWrapper to new service- i can mockNewService.Verify(x => x.TryCatchCallWrapper) but if i do like you offer - how can i check TryCatchCallWrapper called in one line? – arielorvits Feb 09 '16 at 09:47
  • my suggestion is good only for testing internals of TryCatchCallWrapper. sounds like you need to extract your function to be an implementation of a new interface and in the tests you should provide a mock object that expects a call on this function, – silver Feb 09 '16 at 12:42
1

An option is to make the method internal, then expose it to your test suite using the InternalsVisibleToAttribute allowing your test suites to view internal members of your class.

Add the following to AssemblyInfo.cs in your project,

[assembly: InternalsVisibleTo("mytestassembly)]

If your assembly is signed, it is a bit more work. You need to also sign your test assembly and add the PublicKey of your test assembly to the attribute. You can get the public key using sn.exe -Tp mytestassembly.dll from a Visual Studio Command Prompt, then add it to the attribute like this;

[assembly: InternalsVisibleTo("mytestassembly, PublicKey=002400000480000094" +
                         "000000060200000024000052534131000400000100010031eea" +
                         "370b1984bfa6d1ea760e1ca6065cee41a1a279ca234933fe977" +
                         "a096222c0e14f9e5a17d5689305c6d7f1206a85a53c48ca0100" +
                         "80799d6eeef61c98abd18767827dc05daea6b6fbd2e868410d9" +
                         "bee5e972a004ddd692dec8fa404ba4591e847a8cf35de21c2d3" +
                          "723bc8d775a66b594adeb967537729fe2a446b548cd57a6")]

You can also look at this answer for InternalsVisibleTo.

You also don't mention anything about your Login() method. I assume that you are using dependency injection to mock out the functionality in the login?

Community
  • 1
  • 1
Rob Prouse
  • 22,161
  • 4
  • 69
  • 89
1

I would suggest a refactor. Move the code that you want to test to separate class ExternalLibCaller and inject the caller object into main class. Then you can create:

  • tests for ExternalLibCaller to test reactions for exceptions,

  • tests for main class with ExternalLibCallerMock to check if SafeCall was executed,

  • tests for main class with ExternalLibCaller to check whole external lib API.

Example:

public class ClassWithPrivateTryCatchCallWrapper
{
    IExternalLibCaller externalLibCaller;

    public ClassWithPrivateTryCatchCallWrapper(IExternalLibCaller externalLibCaller)
    {
        this.externalLibCaller = externalLibCaller;
    }

    private T SafeCallWithLogin<T>(Func<T> method)
    {
        externalLibCaller.SafeCall(() =>
        {
            Login();
            return method();
        })
    }

    //use: SafeCallWithLogin(method)
    //insted: TryCatchCallWrapper(method);
}

public interface IExternalLibCaller
{
    T SafeCall<T>(Func<T> method)
}

public class ExternalLibCaller : IExternalLibCaller
{
    public T SafeCall<T>(Func<T> method)
    {
        try
        {
            return method();
        }
        catch (ConnectionException e)
        {
            switch (e.ConnectionReason)
            {
                case ConnectionReasonEnum.Timeout:
                    throw new ...
            }
        }
        catch (XException e)
        {
            throw ...
        }
    }
}
r2-
  • 11
  • 3