0

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?

2 Answers 2

4

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.

2
  • thank you for the succinct and well explained answer, much appreciated. I think I've got the idea now. :-)
    – Imran Azad
    Commented Mar 23, 2018 at 10:00
  • I think most reasonable people agree on what a unit tests is and the myriad benefits. It is when people try to stretch the definition into other sorts of testing where the problem lies.
    – Robbie Dee
    Commented Mar 23, 2018 at 10:37
1

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)

4
  • 1
    Good unit tests actually do test behaviour (in a GWT style) but you're right - they don't go across layers. Consider also that it perfectly possible to have unit tests without classes (in non-OO languages).
    – Robbie Dee
    Commented Mar 23, 2018 at 10:12
  • "There is a school of thought that a unit test tests some behaviour across layers but this isn't correct". This is a nonsense statement. You are not gifted with the absolute right to say whether another's opinion on a subjective matter is correct or not. You are free to disagree, but that doesn't make it incorrect. I like the FIRST acronym though. Swap out "Independent" and replace with "Isolated" and it's an excellent set of guidelines to good unit tests.
    – David Arno
    Commented Mar 23, 2018 at 10:16
  • 1
    @DavidArno "Swap out "Independent" and replace with "Isolated"" I'd vote to keep both unfortunately breaking the cool acronym... Commented Mar 23, 2018 at 10:59
  • There is another cool acronym describing features of unit tests (sort of overlapping yours): Readable, Trustwothy, Fast and Maintainable. Commented Mar 23, 2018 at 11:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.