Question

I'm trying to better understand unit tests and integration tests. I understand unit tests should be in isolation and their dependencies should be mocked. But I'm slightly unclear on the best approach to assert that each class unit tested integrates with it's dependencies.

Here is an example:

I have a class called ElasticDocumentFeeder which basically calls elastic's HTTP end point and indexes a document. There's a method called AddDocument(Document document, logger)

This method simply calls the endpoint using the .net HTTP client with a PUT to index a document. If the response code is anything other than a 200, an error is logged.

I want to write a test that asserts that if a document is rejected by elastic, the error is logged.

I've managed to do this using NUnit and Moq and I'm able to assert the invocation on the logger.

What I want to know is: even though I'm testing this class, it does have a dependency. It expects elastic to be running on localhost:9200. So would this be considered a unit test or an integration test?

I know I can abstract the HTTP endpoint so that I could mock an exception that is thrown by elastic. Do you think this would be the more correct approach and as a result it would truly be a unit test? Or is it fine to have something like elastic running with a unit test with regard to being pragmatic?

Should I have an end-to-end integration test which would cover everything? I.e. a message would be picked up from rabbit, then a call would be made to instantiate MessageProcessor then ElasticDocumentFeeder etc.? Should I test each class individually against all its dependencies with regard to integration tests? Or is this all overkill, as I would then need an instance of rabbit and elasticsearch?

Was it helpful?

Solution

The difficulty in answering this question is that there is no fixed definition of what unit and integration tests are.

The best definition I've come across is that a unit test is one that runs in isolation, ie it has no side effects or stateful external dependencies and can be run in parallel with any other unit test. There is a useful addendum to this to, that a unit test should be fast. Everything else is then an integration test.

Using the above definition, your test is an integration test.

So then we get on to the question of, since it's an integration test, should it be less isolated? I think it's folk from Thoughtworks that sum up mocking frameworks: they aren't harmful in themselves, it's the way they are (mis)used that can make them harmful. Mocking external dependencies that can make tests brittle, such as file or URL access, is a good thing. Needlessly mocking out one part of your own code when testing another part can be bad though. This is taking the quest for testing isolation to extremes.

From the way you describe you situation, you aren't falling into this trap though; you are just minimising the external interactions. As such, this sounds like a useful integration test, as is. However (there's always a "however"), your scenario of doing an end-to-end test sounds good too. So I'd say have both. Just beware of the end-to-end test being slow and fragile and thus potentially not being suitable for running alongside your unit test suite on each build and check-in.

OTHER TIPS

A unit test typically tests some aspect of a class (in OO languages). There is a school of thought that a unit test tests some behaviour across layers but this isn't correct (that is an integration test).

Good unit tests have a number of features - defined by the FIRST acronym.

Fast

Unit tests should be fast

Independent

It should be possible to run them independently of any dependencies

Repeatable

A test should be repeatable with no manual cleardown or set up

Self-checking

A test should be able to check its own result automatically

Timely

Tests should be written at the same time as the code under test (CUT)

Licensed under: CC-BY-SA with attribution
scroll top