views:

285

answers:

5

Hi!

I’m having a mocking problem. I know only public methods should be mocked. But what do I do when a private method is called in a public method, and this private method is getting data from a file? I want to mock this private method so I could go on in with the test of the public method.

I could make this public to make the test work, but that would make no sense since it is private for a reason. I could also move everything into another class and make the functions public there, but the reference to the object in the main class should either way be private.

I'm using Rhino Mocks.

Thanks for any help :)

A: 

Pull the file dependency out and pass in a IFileSomething interface in the constructor. Then mock the IFileSomething and set an expectation on it.

Hans Malherbe
+3  A: 

If you put the functionality that does the file reading into a class e.g

FileReader : IFileReader

Then pass in IFileReader as an arg to the constructor. you can then mock that

David Archer
I voted up your answer because it works, but I see the following drawbacks, feedback is welcome:- A single IFileReader must persist with the lifetime of the object when it may just be used in a single function- It makes the object more state-based than it has to be. The IFileReader will need to be reset between subsequent function calls to ensure the function behaves the same way each time it is called, this introduces additional complexity.- It also causes limitation that only one IFileReader can be used at a time, unless there is a way to copy the object.
CiscoIPPhone
You're right, but reading from a file is like fetching from a repository and I believe that functionality should then be separated. Perhaps if you use dependency injection like Windsor, they might have built in caching of some sort. I really don't know for sure though.
David Archer
Thanks for the answer! I see that it work, but I would prefer not to have it as an arg to the constructer, because of the drawbacks CiscoIPPhone also mentions. I would need to do that for every object I use for external data in private methods, I need some more for other private methods. What do people usually do when they have to mock private method calls in a public method?
I still think that is correct that the functionality for fetching should always be separated. Yes it makes testing more involved but then you truly are testing functionality of that object e.g controller. If down the line you started using a database instead of the files, you would then just the concrete implementation of IFileReader (now a bad name choice), with a class that fetches from db. Dependency injection frameworks can take care of passing the correct object at runtime, but that obviously doesn't help your test issue
David Archer
A: 

An alternative to the other suggestions is to make your code templated and receive a FileReader type which could be used throughout the methods of your class.

template <class FileReader>
class SomeClass
{
  private: void doSomething()
  {
    FileReader fileReader;
    // Do something
  }
};

Another way would be to pass a method into the SomeClass constructor that returns an implementation of a FileReader. This could be used throughout the class similar to how the template is used, but doing it this way you'll still derive your MockFileReader from IFileReader.

The problem with either one of these is that unit tests can't be performed on the FileReader because there is no access to it from SomeClass.

n.b. the code above is C++ but I know both methods are doable with C#.

CiscoIPPhone
A: 

Make sure that you agree with yourself wether this is a service or business entity.

A service is usually recognized in that it uses a set of business entities to get some task done and that it does not itself maintain durable data.

A business entity is a unit in your problem domain, something you would conceptually like to persist.

If you find that it is a service, you should somehow inject the dependency when you construct the service. Typically it could have a constructor like this: public MyService(IFileObject)

When constructed from main: var service = new MyService(MyRealFile)

When constructed from Test setup: var service = new MyService(MyMockedFiled)

If you find that what you are testing is actually a business entity, then you should avoid giving the Entity a dependency. You typically do this be taking one step back and construct a Service class between yourself and the business entity. The service explicitly hands the business entity all its necessary data. In your case, this means that the service provides the entity with whatever it is supposed to learn by reading the file.

The Service is thus the one with the dependency to the file system and may even use another (dedicated) filereader business entity to read the file. You would never like business entities to make rogue dependency calls if you use it somewhere else in the system. Their code becomes context bound, which is something you would like to avoid. Business entites shall be fast, discrete, defined and responsive.

Your unit test shall be to the service, which may receive a dependency by injection. If you find that you are unit testing a business entity, then you are lacking the service level.

Tormod
A: 

Thanks for the answers, my cookie got deleted. I used the first answer and changed a bit in the code. Works well :)