spockframework / spock

The Enterprise-ready testing and specification framework.
https://spockframework.org
Apache License 2.0
3.54k stars 467 forks source link

Grouping specs with shared setup/teardown #106

Open YaroSpace opened 9 years ago

YaroSpace commented 9 years ago

I am testing my grails applications extensively with Spock. Thank you guys for a great tool. One thing that I am really missing from ruby's Rspec and JS mocha/jasmine is the describe/context/it scoping, e.g.

describe 'UserController#index'
  before {
    common setup steps 
  }

  context 'user is logged in' {
     before {
       setup steps for this context
     }

     it 'returns 200' {
      test
     }

     it 'creates ..' {
     }

   }
}

Extracting common setup logc isn't problem, but readability is greatly improved. Will or can this be implemented for spock? Or, perhaps, there is a sane workaround, without creating a separate Spec class for each context?

robfletcher commented 9 years ago

While I agree this is something notably missing from Spock when compared with RSpec, Jasmine, certain flavors of Scalatest, etc. I'm going to argue that dividing things into multiple spec classes is actually a good thing!

Small, well named specs that describe behavior rather than just being named for the units of code they test enhance the clarity of the test suite. When it starts to get difficult to reconcile the setup/cleanup logic of multiple feature methods I take that as a signal that it's time to split things out into a new spec class.

If you really want to have everything in a single file you can have multiple classes in a single .groovy file. I'm ambivalent as to whether that's a good idea but it's really nothing Spock can encourage or discourage.

There's also a way of doing something like this with inner classes (granted it lacks the neat DSL of RSpec) see http://spock-framework.3207229.n2.nabble.com/Grouping-several-specs-in-blocks-inside-one-Specification-class-tp7574088p7574090.html

If we were to consider this I think it would have pretty big implications for the test runner and reporting. Right now each invocation of a uniquely named feature method counts as one test for reporting purposes whereas in this kind of scheme you'd want to output a tree-like report.

leonard84 commented 9 years ago

I agree with @robfletcher.

This kind of grouping often leads to huge and hard to understand specs, simply because instead of using inheritance we just put everything related to Foo into FooSpec. IMHO it is better to split those specs into smaller sub specs with proper naming logical coherence.

I've adapted robs code to your example, as you can see you can already do something similar like it with Spock. Granted without syntactic sugar and some reporting issues. However, I would suggest moving the nested classes to toplevel classes in their own file.

class UserControllerSpec extends Specification {

  def setup() {
    // common setup steps 
  }

  def "a feature"() {
    expect: 1 == 1
  }

  static class LoggedInUserControllerSpec extends UserControllerSpec {

    def setup() {
      // common setup steps 
    }

    def "returns 200"() {
      expect: userRequest.status == 200
    }
  }
}

So bottom line, I'm not convinced that this would be a good addition to Spock.

jirutka commented 9 years ago

I think that this is like with parametrized tests. You can somehow parametrize specs in RSpec, sure, but it’s often odd and harder to read, it’s just not intuitive (same as "contexts" in Spock). Look at Spock, it provides so awesome support for parametrized tests* that it’s perfectly natural and very intuitive to parametrize tests. I can imagine that RSpec guy may argue against Spock-like parameterization in the same way as @robfletcher against contexts (e.g. you guys combines multiple specs in one)!

Every feature can be overused or misused. Contexts are not about placing hundred-lines tests into one file, not at all.

* I personally consider this as the most awesome feature of Spock. Right behind are mocks. These two features beats RSpec.

YaroSpace commented 9 years ago

I do not think the argument that 'contexts' would encourage to put all the specs in one file really applies here. One should always be sensible with amount of code/specs that he has in a file/class/method and structure things appropriately.
For me it is not the matter of putting things in one file, but grouping specs (even just 2 or 3 of them) under a common meaningful description, e.g. these 2 specs when user logged-in and these 2 when he is not.
It is a basically a 'documentation' feature. Jumping across a bunch of small files to understand the flow of the thing under test can become tedious.

Thank you @leonard84 for giving an example how this can be done currently. A bit verbose, but will do the job.

P.S. Totally agree on parametrized tests - given/when/then + parameters in a table are a great take from cucumber. We needed that in unit/integration tests.

rajeevgautam commented 7 years ago

Hi All,

Do we have anything using which we can share common setup among all specifications classes like 'SuiteSetup' and executed only once.
For my tests i have to enable some service before i execute my test. I have my tests spread over multiple classes. I want to enable and disable it only one time during test execution. Hence cannot use setup or setupSpec. As both will do it more than once.

Please if you can advise.

leonard84 commented 7 years ago

Hi,

JUnit controls how Specs are executed, Spock only controls execution in a Spec. That means that Spock does not have an awareness of suites or other things like that. So you can achieve something like an setupOnce by using a Helper class with a static setupOnce method which you would call in each setupSpec and the setupOnce will take care of only executing the init code once. However, you can't have a corresponding cleanup method, since Spock does not know what belongs together.

But there might be a way by combining Spock with JUnit here, since Specs are basically JUnit tests you can use JUnit's suite to group up Spock tests. I haven't really tested, especially if it would execute the tests twice (once as normal tests and again inside the suite), so you'd need to filter only for suites.

@RunWith(Suite.class)
@Suite.SuiteClasses([Spec1, Spec2])
class SuiteTest {
}
behrangsa commented 5 years ago

Hi all,

Any updates on this? A method to group tests similar to RSpec, Mocha, Jasmine, and the like?

RSpec example:

describe Course do
  context "when user is logged in" do
    it "displays the course lessons" do
    end

    it "displays the course description" do
    end
  end

  context "when user it NOT logged in" do
    it "redirects to login page" do
    end

    it "it shows a message" do
    end
  end
end

Mocha example:

describe('Array', function() {
  describe('#indexOf()', function() {
    it.only('should return -1 unless present', function() {
      // ...
    });

    it('should return the index when present', function() {
      // ...
    });
  });
});