Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I prevent my unit tests from requiring knowledge about implementation internals when using mock objects?

I'm still in the learning stages regarding unit-testing and in particular regarding mocking (I'm using the PascalMock and DUnit frameworks). One thing I now stumbled over was that I couldn't find a way around hard-coding implementation details of the tested class/interface into my unit test and that just feels wrong...

For example: I want to test a class that implements a very simple interface for reading and writing application settings (basically name/value pairs). The interface that is presented to the consumer is completely agnostic to where and how the values are actually stored (e.g. registry, INI-file, XML, database, etc.). Naturally, the access layer is implemented by yet a different class that gets injected into the tested class on construction. I created a mock object for this access layer and I am now able to fully test the interface-implementing class without actually reading or writing anything to any registry/INI-file/whatever.

However, in order to ensure the mock behaves exactly like the real thing when accessed by the tested class, my unit tests have to set up the mock object by very explicitly defining expected method calls and the return values expected by the tested class. This means that if I should ever have to make changes to the interface of the access layer or to the way that the tested class uses that layer I will also have to change the unit tests for the class that internally uses that interface even though the interface of the class I'm actually testing hasn't changed at all. Is this something I will just have to live with when using mocks or is there a better way to design the class-dependencies that would avoid this?

like image 942
Oliver Giesen Avatar asked Aug 10 '10 10:08

Oliver Giesen


People also ask

What do you have to avoid in tests in unit testing?

Avoid Logic in Tests Doing so reduces the chance of introducing bugs into the test. The focus must remain on the end result, not on circumventions of implementation details. Without too many conditions, tests are also likely to be deterministic.

Is mocking good for unit testing?

Advanced: Mocking in Unit TestMocking is useful to replace operations that should not be run in a testing environment, for instance, to replace operations that connect to a database and loads data when the testing environment does not have the same data access.

Is mocking bad in unit testing?

Automated testing during software development involves many different techniques, one that shouldn't be used is mocking. Mocks are a distraction at best and provide false confidence at worst.

What is required if we need to use mock data for a test?

So in order to use mock data effectively, it is essential to have a good understanding of the software under test and more importantly how it uses its data. To start using mock data the software under test needs to be “tricked” into replacing real data with fake data.


2 Answers

to ensure the mock behaves exactly like the real thing when accessed by the tested class, my unit tests have to set up the mock object by very explicitly defining expected method calls and the return values expected by the tested class.

Correct.

changes to the interface of the access layer or to the way that the tested class uses that layer I will also have to change the unit tests

Correct.

even though the interface of the class I'm actually testing hasn't changed at all.

"Actually testing"? You mean the exposed interface class? That's fine.

The way the "tested" (interface) class uses the access layer means you've changed the internal interface to the access layer. Interface changes (even internal ones) require test changes and may lead to breakage if you've done something wrong.

Nothing wrong with this. Indeed, the whole point is that any change to the access layer must require changes to the mocks to assure that the change "works".

Testing is not supposed to be "robust". It's supposed to be brittle. If you make a change that alters internal behavior, then things can break. If your tests were too robust they wouldn't test anything -- they'd just work. And that's wrong.

Tests should only work for the exact right reason.

like image 60
S.Lott Avatar answered Oct 31 '22 01:10

S.Lott


Is this something I will just have to live with when using mocks or is there a better way to design the class-dependencies that would avoid this?

A lot of times mocks (particularly sensitive frameworks like JMock) force you to account for details that don't relate directly to the behavior you're trying to test, and sometimes this can even be helpful by exposing suspect code that is doing too much and has too many calls/dependencies.

However in your case, if I read your description right, it sounds like you really don't have a problem. If you design the read/write layer correctly and with an appropriate level of abstraction, you shouldn't have to change it.

This means that if I should ever have to make changes to the interface of the access layer or to the way that the tested class uses that layer I will also have to change the unit tests for the class that internally uses that interface even though the interface of the class I'm actually testing hasn't changed at all.

Isn't the point of writing the abstracted access layer to avoid this? In general, following the Open/Closed principle, an interface of this sort shouldn't change and shouldn't break the contract with the class that consumes it, and by extension it won't break your unit tests either. Now if you change the order of the method calls, or have to make new calls to the abstracted layer, then, yes, particularly with some frameworks, your mock expectations will break. This is just part of the cost of using mocks, and it's perfectly acceptable. But the interface itself should, in general, remain stable.

like image 27
Dave Sims Avatar answered Oct 31 '22 01:10

Dave Sims