views:

590

answers:

9

I understand the need to test a class that has logic (for instance, one that can calculate discounts), where you can test the actual class.

But I just started writing unit tests for a project that will act as a repository (get objects from a database). I find myself writing a 'fake' repository that implements an ISomethingRepository interface. It uses a Dictionary<Guid, Something> for storage internally. It implements the Add(Something) and GetById(Guid) methods of the interface.

Why am I writing this? Nothing I'm writing will actually be used by the software when it's deployed, right? I don't really see the value of this exercise.

I also got the advice to use a mock object that I can setup in advance to meet certain expectations. That seems even more pointless to me: of course the test will succeed, I have mocked/faked it to succeed! And I'm still not sure the actual software will perform as it should when connecting to the database...

confused...

Can someone point me in the right direction to help me understand this?

Thank you!

A: 

You write "fake" class called Stub or Mock object because you want to test an implementation in a simple way without testing the real concrete class. The purpose is to simplify the testing by testing only the Interface (or abstract class).

In your example, you are testing something that has a dictionnary. It might be fill up in real by the database or have a lot of logic behind it. In your "fake" object, you can simplify everything by having all data constant. This way you test only the behavior of the interface and not how the concrete object is built.

Daok
I understand, but how is that valuable? I wrote the *fake* and tested it. It is not used in production code anywhere. So why write it and test it?
michielvoo
To test your implementation without having in your path concrete element. You can take out a lot of code and concentrate on the purpose of your testing. My example in my post is good... the databse one... you do not need to have a "real database" and you can fake it...
Daok
+2  A: 

The purpose of the mock/stub object is not to be tested instead of the unit you're trying to test, it's to allow you to test that unit without needing other classes.

It's basically so that you can test classes one at a time without having to test all the classes they're also dependent on.

Will Dean
Yes, that's what I thought. So in a 'higher' layer that normally uses the repository code, I could inject a mock for testing so it will not actually connect to the database, correct?
michielvoo
Right on that one
Mario Ortegón
+12  A: 

You are not testing your mock object but some other class that is interacting with it. So you could for example test that a controller forwards a save method call to your fake repository. There is something wrong if you are "testing your fake objects"

mmiika
Yeah, that's what I thought. So then I have the wrong idea about testing my data access code. For example: http://tinyurl.com/6qojvh
michielvoo
Sorry, is this a question?
mmiika
Well, I was kind of wondering what your opinion was on that article in the context of our discussion.
michielvoo
+1  A: 

Who watches the watchers?

It is interesting for example if the mock implementation throws specific Exceptions for the corner cases, so you know that the classes that use or depend the IRepositorySomething can handle the exceptions that are thrown in real life. Some of these exceptions you can't generate easily with a test database.

You do not test the Mock object with a unit test, but you use it to test classes that depend on it.

Mario Ortegón
I've found creating exceptional circumstances to be one of the most useful things you can do with a mock. +1
Daniel M
+2  A: 

Don't test the mock class. Do test the production class using the mock class.

The whole point of the test support class is to have something that you can predict its behavior. If you need to test the test support class in order to predict its behavior, there is a problem.

In the fake database article you linked in a comment, the author needs to unit test his fake database because it is his product (at least in the context of the article).

Edit: updated terms to be more consistent.

  • Mock - created by mocking framework
  • Fake - created manually, might actually function some.
  • Test Support - Mocks, Fakes, Stubs, and all the rest. Not production.
David B
"Do test the production class using the mock class"Ok, so if my production data access class uses NHibernate, I should mock NHibernate, and then test the production class? So the NHibernate part of the production class should be injectable. And no need to write a fake data access class?
michielvoo
Right. you should mock NHib, and don't need to provide a fake. Unless NHib's public contract is too broad... then you should wrap NHib and mock the wrapper.
David B
But since your dataacess already wraps NHib from the rest of the app... mock your dataacess to test the rest of the app (most value).
David B
+1  A: 

Ok, so what I'm seeing here and here (and many other places) should be ignored?

michielvoo
Ah right, not really. If you are writing your own fake repository like that, you would test it as well to make sure that it behaves like expected. This is to make sure that other tests that will be using that fake will fail/success for correct reasons.
mmiika
+1  A: 

You should not be testing the mock class.

What you normally do is: you create mock classes for all the classes that the class you are testing interact with.

Let's say you are testing a class called Bicycle which takes in the constructor objects of classes Wheel, Saddle, HandleBar,etc.

And then within the class Bike you you want to test test its method GetWeight which probably iterates through each part and calls property/method Weight of them and then returns the total.

What you do:

  • you write a mock class for each part (Wheel, saddle etc) which simply implements the Weight bit
  • then you pass those mock classes to the Bicycle
  • test the GetWeight method on the Bicycle class

It that way you can focus on testing the GetWeight on the Bicycle class, in a manner that is independent on other classes (say they are not implemented yet, not deterministic etc.)

kristof
A: 

Instead of writing a fake class by yourself, you can use a tool (like Rhino or Typemock) to mock it. It is much easier than writing all the mocks yourself. And like others said, there's no need to test fake code, which is no code if you use the tool.

A: 

I have actually found two uses for the mock classes that we use in repository implementation testing.

The first is to test the services that use an implementation of the "ISomethingRepository" equivalent that you mention. However, our repository implementations are created by a factory. This means that we do write tests against the "ISomethingRepository", but not against the "MockSomethingRepository" directly. By testing against the interface, we can easily assert that the code coverage for our tests cover 100% of the interface. Code reviews provide simple verification that new interface members are tested. Even if the developers are running against the mock that the factory returns, the build server has a different configuration that tests against the concrete implementation that the factory returns within the nightly builds. It provides the best of both worlds, in terms of test coverage and local performance.

The second use is one that I am surprised that no one else has mentioned. My team is responsible for the middle tier. Our web developers are responsible for the front end of the web products. By building out mock repository implementations, there is not the artificial obstacle of waiting for the database to be modeled and implemented prior to the front-end work starting. Views can be written that will be built off of the mock to provide a minimal amount of "real" data to meet the expectations of the web developers, as well. For example, data can be provided to contain minimum and maximum length string data to verify that neither break their implementation, etc.

Since the factories we use are wired as to which "ISomethingRepository" to return, we have local testing configurations, build testing configurations, production configurations, etc. We purposely are trying to make sure that no team on the project has unreasonable wait times because of another team's implementation time. The largest chunk of wait time is still provided by the development team, but we are able to crank out our domain objects, repositories, and services at a faster pace than the front-end development.

Of course, YMMV. ;-)

joseph.ferris