views:

460

answers:

6

I have a class called "Session" which exposes several public methods. I'd like to Unit Test these, however in production I need to control instantiation of "Session" objects, so delegate construction to to a SessionManager class and have made the Session's constructor internal.

I'd ideally like to test the Session class in isolation from the SessionManager which creates it/them to prove that the public interfaces exposed by the Session work as expected - but can't instantiate a Session from a test without using a SessionManager making my tests more complicated/less useful than they need to be.

What's the best way to deal with this?

Cheers,

Lenny.

+3  A: 

You could just make your unit test class inherit from Session (assuming your test framework doesn't require that you inherit from a specific class). For instance, with NUnit :

[TestFixture]
public class SessionTest : Session
{
    public SessionTest()
        : base() // call protected constructor
    {
    }

    [Test]
    public void TestSomething()
    {
    }

}
Thomas Levesque
OP said ctor is internal, not protected.
Rex M
@Rex - the title of the question says protected, while the question itself says internal.
Jeff Sternal
A: 

You want to use a product like TypeMock, which will allow you to create mocked (faked) instances of classes, like this:

var myInstance = Isolate.Fake.Instance<Session>();
// mock behavior here
// do assertions here

You can create instances of abstract classes with TypeMock as well, for the record.

Josh Kodroff
mocking the same class you want to test doesn't make much sense to me though... wouldn't you be testing the mock and not the class then?
Svish
@Svish, you're correct that faked behavior is not useful, but it can also create real instance and solve the accessibility issue: Isolate.Fake.Instance<Session>(Members.CallOriginal). This way you have an actual instance of the class with who's constructor is inaccessible.Disclaimer - I work at Typemock.
Elisha
+2  A: 

Alternatively, as a workaround, you could just create a TestSession that inherits from Session and exposes a public constructor. Inside your unit-test you then use the TestSession which basically does the same as the original Session object.

public class TestSession : Session
{

   public TestSession() : base()
   {

   }

}
Juri
+1  A: 

I would question the value of making the session class constructor internal.

Generally this implies you are trying to stop other developers from using your class. Maybe it would be better to communicate how this particular piece of the application works and why you would only want to access the session from the session manager?

Derek Ekins
I've seen it done a lot with a factory model, where you might want to enforce that the class is only ever created or destroyed in one place, but other people are allowed to reference it and use it.
Russell Newquist
I have seen it a lot as well :) I just don't see how it adds any value. It just causes problems when you want to extend or test - just as Leonard is experiencing now.MS is one of the worst offenders in this regard, there are a ton of internal classes that would be useful to use sometimes, but we just can't access them! Frustraing!
Derek Ekins
As someone who used to be a consumer of APIs, and was very frustrated like you wondering why MS makes so many things internal, protected, etc. Now I do primarily API development and my stuff is consumed by other developers. I spend waaaaay to much time cleaning up other people's messes because they get themselves in trouble using stuff I should've made internal. Sometimes it's just not realistic to expect to be able to communicate "proper" use of every piece of code we write.
Rex M
Another way to look at it is by making something public/virtual/not sealed, I am explicitly making a contract with everyone who touches that class - "this is extensible and will work properly." I have to take many extra steps to ensure that is something I can live up to - which is not always cost-effective. So when that guarantee cannot be made, it stays internal/sealed.
Rex M
fair enough, although for the majority of devs who are not writing APIs I think my point still stands.
Derek Ekins
A: 

If you can leave with "protected internal" constructor, then, assuming that your tests are in different assembly, you can add InternalsVisibleTo attribute to your "to-be-tested-assembly". This would allow your tests to see internal members of to-be-tested-assembly".

Update For some reason I read that your constructor was protected. It is already internal so you are in a good position to use that attribute.

epitka
+12  A: 

Nothing prevents you from testing internals. Simply make the internals of your code visible to the test suite, by using the InternalsVisibleTo attribute: in the AssemblyInfo, add

[assembly:InternalsVisibleTo("TestSuiteAssembly")]
Mathias
This seems to be the easiest way to do it, so I think I'll run with this. Thanks.
Leonard H Martin