Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integration and unit tests in large Grails project

Tags:

grails

groovy

It is usually more complicated to write unit tests due to having to deal with mock objects than integration tests in a large Grails project. This article even suggests we can even do away with unit tests altogether and write only integration tests which I tend to agree.

The only disadvantage I see is the speed of execution for integration test as compared to same unit test.

What are your thoughts about this from your actual experience working on a large scale Grails project?

If we write a unit test that tests exactly same method and also write integration test that also tests exactly same method, is this normal way of writing tests?

What you ended up with in terms of ratio of unit tests to integrations tests in actual large Grails project?

Have you successfully completed a large Grails project without writing any tests?

like image 278
ace Avatar asked Apr 10 '11 08:04

ace


People also ask

Do we need unit tests and integration tests?

Unit Testing is a kind of white box testing, whereas Integration Testing is a kind of black-box testing. For Unit Testing, accessibility of code is required, as it tests the written code, while for Integration Testing, access to code is not required, since it tests the interactions and interfaces between modules.

Are integration tests better than unit tests?

While unit tests always take results from a single unit, such as a function call, integration tests may aggregate results from various parts and sources. In an integration test, there is no need to mock away parts of the application. You can replace external systems, but the application works in an integrated way.

What is unit and Integration Testing?

Unit Testing is typically performed by the developer. It is a testing method using which every independent module are tested to determine if there is any issue by the developer himself. Integration Testing: Integration testing is the process of testing the interface between two software units or modules.

What's the difference between unit integration and E2E testing?

Unit tests are the least complex and E2E tests are the most complicated. We tend to write more tests with less complexity. It's preferable to write more unit tests than E2E tests. The integration tests fit in the middle.


2 Answers

I always write my tests as unit tests if possible. I do this because:

  • unit tests execute quicker
  • I prefer to test each component in isolation, rather than testing all the components integrated together, because this makes it easier to identify the source of an error
  • the unit testing environment is simpler (e.g. no Spring application context), so there are fewer potential sources of failure that are unrelated to the test being performed

An example of where I would write an integration test is if I want to test a Spring bean that I've defined in resources.groovy. Although I could instantiate the class and test it directly, my test would then need to know the current implementation class of that bean (which might change).

In the long-run I think it's actually more complicated to write integration tests, because the cost of maintaining them over time is higher than unit tests. Groovy/Grails has excellent support for mocking/stubbing, so the cost of mocking dependencies in unit tests is relatively low. Here's a real-world example from one of my unit tests where I:

  • mock the messageSource Spring bean that would normally only be available in an integration test
  • mock two command classes such that I can call the validate() method, inspect the .errors property, etc.

class MyUnitTests extends GrailsUnitTestCase {

    MessageSource messageSource

    protected void setUp() {

        super.setUp()
        // mockForConstraintsTests is a method provided by GrailsUnitTestCase
        [Complex, CategoryCommand].each {mockForConstraintsTests(it)}

        // 'mockMessage' will be returned by every method call on messageSource
        messageSource = {Object[] args -> "mockMessage"} as MessageSource
    }
}
like image 197
Dónal Avatar answered Oct 16 '22 16:10

Dónal


I've worked on 3 large grails apps, and innumerable smaller ones. My current project has a mix of unit and integration tests (currently 2110 unit tests and 493 integration tests).

I've spent a bunch of time trying to improve testing speed as well as making tests more maintainable.

My integration tests are often a hybrid where if I'm testing a service, I might mock out some other services/methods being called to ensure I get the values that I want, but leave in other integration pieces to exercise HQL or database integration. To this end, I use prototype instances of what are normally singleton services so that I can muck with the service instance without polluting later tests.

I find the build-test-data plugin invaluable for creating maintainable unit tests as it lets me create test data where I explicitly populate the pieces I need and let the plugin fill in the other required details. Creating test data in integration tests is easier for me than mocking it out in unit tests.

If you do use both integration and unit tests, eventually the speed of running all of the tests serially will become an impediment. My team uses the splitTests.groovy script to spin off two separate threads, one for unit tests, one for integration tests. This exercises our tests about 40% faster. Further parallelization is possible, but we haven't gone there yet (and the current grails gant scripts are pretty nasty under the covers, I'm looking forward to the gradle rewrite in grails 2.0).

Unit tests are nice for hitting all of the conditional nooks and crannies of a method (though if you've got too many, your cyclomatic complexity is probably too high and you should refactor). Integration tests are useful for exercising database and service integration as well as helping you understand what you've broken when you change a piece of code.

I think that the refactoring courage that you get from having high test coverage is partly dependent on some of the tests being integration tests. If all you have are unit tests that don't interact with other code pieces, you won't be alerted of affected areas when you make a code change, and because groovy is a dynamic language, the compiler likely won't help you find these areas either.

like image 24
Ted Naleid Avatar answered Oct 16 '22 15:10

Ted Naleid