chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
0 votes
Is it possible to Isolate a private class? For example with duck typing or anything? Without creating private accessors?

For example, how would I isolate the call to Logger.Write("") ???

public class SomeClass
{
   public void DoSomething(Customer customer)
   {
      if (customer.IsPaying)
      {
         Logger.Write("Let's start invoicing");
         Invoicing.StartInvoicing(customer.CustomerId);
      }
   }
}

private class Logger
{
   public static void Write(string msg)
   {
      throw new NotImplementedException();
   }
}
asked by dvdstelt (5.3k points)

12 Answers

0 votes
Hi Dennis

I'm not sure I understand your example.
The Logger class can't be private unless it's internal class
Can please explain what are you trying to fake here?

Note that you can fake private accessors using natural mocks API
Currently this is not supported by AAA API. :(
answered by ohad (35.4k points)
0 votes
Hi Ohad,

Thanks for the link.

I made the class internal and used the InternalsVisibleTo attribute in AssemblyInfo.cs to make Logger visible to other assemblies, like my test project and Isolator.

I could also use accessors, I'm currently deciding what to do. Maybe go for the latter, because than I won't have to change anything in my tested assembly.

Thanks
answered by dvdstelt (5.3k points)
0 votes
If you were to make the Logger class nested inside the SomeClass, you could still make it a private class... and you can still mock it. You just have to use Reflective mocks.

Here's an example showing that. I simplified it a bit to get rid of the "Customer" and "Invoicing" types, but you'll get the idea.

public class SomeClass
{
  public void DoSomething()
  {
    Logger.Write("Let's start invoicing");
  }

  private class Logger
  {
    public static void Write(string msg)
    {
      throw new NotImplementedException();
    }
  }
}

[TestFixture]
[VerifyMocks]
public class MyTestFixture
{
  [Test]
  public void MyTest()
  {
    // Note the "+" is how you get a nested type via Reflection.
    Type loggerType = Type.GetType("SomeClass+Logger");
    Mock mockLogger = MockManager.Mock(loggerType);
    mockLogger.CallStatic.ExpectCall("Write");
    SomeClass testObj = new SomeClass();
    testObj.DoSomething();
    // This won't throw an exception because the call to
    // the logger is mocked.
  }
}
answered by tillig (6.7k points)
0 votes
Travis,

Great tip! (I forgot we could do that :oops:)

Thanks,
answered by gilz (14.5k points)
0 votes
Hello! Thank you for this discussion I think I am 99% finished writing my test. I just have one little problem that I can't figure out.
Please see the test below.

An exception is thrown when I run the test below which tries to mock a static method on a private nested type. When I break on the line starting with " Mock mockSomeNestedType ..." the localNestedType variable is populated with the appropriate Type information. Upon running the line starting with " Mock mockSomeNestedType ..." the test throws the following exception: System.ArgumentOutOfRangeException: Length cannot be less than zero. How can I correct this? Thanks!

[TestMethod()]
public void TestSomeNestedType
{
  MockObject mockSomeTypeBase =
    MockManager.MockObject<SomeTypeBase<SomeType>>();

  Type localNestedType = 
    mockSomeTypeBase.MockedType.GetNestedType( "SomeNestedType", 
    BindingFlags.NonPublic );

  Mock mockSomeNestedType = MockManager.Mock( localExistsIdType );  /* THIS LINE THROWS THE ERROR:
* System.ArgumentOutOfRangeException: Length cannot be less than zero.
* Parameter name: length.
*/

mockSomeNestedType.ExpectAndReturn( "SomeMethod", expectedResult, 1 );
  //SOME ADDITIONAL TESTING STUFF...
}
answered by trp (4.1k points)
0 votes
Can you provide the full stack trace? Or, better, a small reproduction that illustrates the issue? I have a feeling the exception message you're seeing is slightly misleading as to what the true error source is. (At least, there's not really a way from the information provided to tell you what's going on.)

Note that I might not use the "mockSomeBaseType.MockedType.GetNestedType" call. In the demo I posted, I grabbed the nested type by doing a standard Type.GetType() call. Try changing to that and see if it fixes the issue.
answered by tillig (6.7k points)
0 votes
I haven't been able to utilize the GetType because my type is complex. The public type that encapsulates the target is a two argument Generic Type, the nested type is private, and the generics all exist in separate assemblies!

public class SomeType<T1>
{
  private class SomeNestedType()
  {
    // Some static Nested Stuff That needs to be Mocked
  }
}


It may be that I'm not getting something right about the Type reference but I can't seem to get the type of SomeNestedType by passing a string. Thank you for your help so far. Can you help me figure out what a valid string would look like for this nested type?

Thanks,
Tom
answered by trp (4.1k points)
0 votes
Generics in reflection are indicated by a backtick followed by the number of generic type parameters.

In the case of your example, that would mean you get the nested type like this:
Type t = Type.GetType("SomeType`1+SomeNestedType");


An easy way to find this sort of information out is to do a little code snippet like this:

public static void Recurse(Type parent)
{
  Console.WriteLine(parent.FullName);
  Type[] children = parent.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
  foreach(Type child in children)
  {
    Recurse(child);
  }
}


I highly recommend tools like SnippetCompiler for this sort of little experiment. It makes running tests like this far, far easier.

Anyway, that code will start at a parent type, dump its name to the console, then find all of the nested types and recurse down the nested type tree, dumping names to the console as it goes.

Then all you have to do is start the recursion. For your test case, it looks like this:

Type root = typeof(SomeType<>);
Recurse(root);


You can learn more about how type names are generated by looking on MSDN at articles like this (though, admittedly, this article doesn't currently cover generics): Specifying Fully Qualified Type Names
answered by tillig (6.7k points)
0 votes
Thanks again for the help. I mocked in another way and got this exception:
*** Cannot instantiate Generic Type Definitions, please specify all type parameters.


This time I did the following:

            Type someTypeInstance = typeof( SomeGenericType<T1> ).GetNestedType( "SomeType", BindingFlags.NonPublic );

            Mock mockSomeType = MockManager.MockObject( someTypeInstance );


I'm not sure what to do now. PS - I still can't get an instance of the type using the GetType( string ) method.

Thanks!
answered by trp (4.1k points)
0 votes
I tried modifying the following lines:

from:
        private class SomeInnerClass : CommandBase

to:
        public class SomeInnerClass : CommandBase


and the lines
from:
        Type someInnerClassDef = typeof( SomeGenericClass<T1>.SomeInnerClass ).GetNestedType( "SomeInnerClass" );

        Mock mockSomeInnerClass = MockManager.Mock( 
                 someInnerClassDef );  // Source of Exception

to:
        Type someInnerClassDef = typeof( SomeGenericClass<T1>.SomeInnerClass );
        Mock mockSomeInnerClass = MockManager.Mock( 
                 someInnerClassDef );  // Source of Exception



Now I get the same error I was getting in the beginning:
Length cannot be less than zero.
Parameter name: length.


How is this happening? Thanks!
-Tom :?
answered by trp (4.1k points)
...