spring-projects / spring-integration-samples

You are looking for examples, code snippets, sample applications for Spring Integration? This is the place.

Home Page:http://www.springsource.org/spring-integration

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Best practices when unit testing in Spring Integration?

darrenwee opened this issue · comments

Is it good practice to use SpringRunner to perform unit tests like in the PetFilterTest example? By this, I mean set up the input and output/discard channels into the component and perform unit tests by sending messages in and seeing what comes out? Do you consider this to be a type of integration testing or a form of unit testing?

I think that it is a sound approach to think of Spring Integration components as the "unit" for unit testing. In this approach, you (unit) test each SI component in isolation to guarantee that the logic is sound, i.e. does this component behave as expected. Integration tests (same thing as the unit test but with > 1 SI components in the middle) are then used to test whether the components are hooked up correctly, i.e. does the message reach this point in the workflow.

Some of my colleagues differ in opinion and think that we should stay out of Spring as much as possible for unit tests. This means we would instantiate the classes manually and test each method individually, so I was wondering if anyone from the team could provide some authoritative opinion. Cheers!

Not sure why you have reported this in the Samples project, but let’s see if this is useful for you: https://docs.spring.io/spring-integration/docs/5.0.7.RELEASE/reference/html/testing.html

Thanks for responding so quickly! The crux of this issue is to find out why the PetFilterTest example was written that way, and whether that test example is considered a unit test or integration test. This seemed like the least wrong place to raise the question. I haven't found a satisfactory answer online nor offline, so this is really a bit of a last resort ask before I chalk up the debate between my colleagues and I to philosophical differences.

As for that manual page: it doesn't really answer my questions because I am not concerned (right now) with what testing tools are available; I am trying to find out what is the most idiomatic way to write unit and integration tests. There is a reference to the Test-Driven Development in Enterprise Integration Projects paper in that manual page, but the section on Unit/Component Testing isn't very explicit.

According Unit Testing description on Wikipedia we really are not restricted with only test-per-method approach when we deal with object-oriented programming.
Therefore, when we omit a @RunWith(SpringRunner.class) we really can consider tests with sending messages to the channels and expecting results from somewhere as a unit test. More over it fits such a model if we treat the flow from -> over(s) -> to as a single unit of work.

I don't see reason to stay away from the @RunWith(SpringRunner.class) for the target project: there are really a lot of Inversion of Control stuff from the ApplicationContext. More over when talk about Spring Integration: it creates a lot of support beans and not all of them are so easy to be replaced with some custom code in the target unit test. Also we need to keep in mind that there is no too much custom code when we build integration flows: target objects are created by the Framework - without Spring container it won't be possible to have something reasonable manually.

You can continue to treat tests with the @RunWith(SpringRunner.class) as integration tests, but that may turn that all your tests for the integration flows are integration tests. Meanwhile unit tests in your projects are going to be only for your custom code.
You also can have a @RunWith(SpringRunner.class), but in the target methods just test some individual beans to be as close to the unit testing as possible. So, it is going to be something like hybrid, but we may close eyes for the @RunWith(SpringRunner.class) since we really would like to unit test beans and we don't test the whole flow, for example.

We treat our tests as integration, when we deal with some external system, like Kafka, RabbitMQ, MongoDb etc.
On the other hand whenever we have the service as an embedded object (e.g. Tomcat, ActiveMQ), we don't treat these kind of tests as integration.

I might be wrong though...

@garyrussell , WDYT?

It entirely depends on what the definition of "unit" is; the first 3 tests in that example would be considered traditional unit tests in that the unit being tested is a java method - a "black box" accepting some input and producing some output; the tests verify the expected output for a given input.

However, the Spring Integration Framework uses configuration to construct an integration flow (similar to how the compiler constructs a method in bytecode).

The integration flow can, itself, be considered a unit - a black box taking some input on the input channel and producing some output. So the tests send some input and expect some output.

So it's all a matter of perspective, IMO.

One other reason I thought was to ensure that the auto-unpacking of the message is done in the way that you expect inside a component, albeit not a major reason. I suppose it is a matter of philosophical difference on the whole. Thanks a lot @artembilan @garyrussell!