Programmatically skip a test in Spock - spock

How do I programmatically skip a test in the Spock framework? I know I can annotate a test with #Ignore to skip it, or use #IgnoreIf to skip tests based on environmental variables and the like. But is there a way to run arbitrary code that decides whether or not a test should run?
For example, let's say I have an integration test that has to connect to a third-party service's sandbox environment. Outages in the service's sandbox environment cause the test to fail. Assuming I've written a method canConnectToService that checks if the test will be able to connect to this service, how do I write a test that will be skipped if canConnectToService() returns false?

Use JUnit's Assume class. Specifically, you could write Assume.assumeTrue(canConnectToService()) at the beginning of your test to skip the test if the third party service is unavailable. This method will throw an AssumptionViolatedException if canConnectToService() returns false, and Spock ignores tests that are interrupted by an AssumptionViolatedException for JUnit compatibility (see this bug report).

There is another alternative (maybe it didn't exists before):
Using instance inside #Requires or #IgnoreIf:
Examples using inheritance, but not required:
abstract class BaseTest extends Specification {
abstract boolean serviceIsOnline()
#Requires({ instance.serviceIsOnline() })
def "some test" () { .. }
}
SubSpecification:
class OnlineTest extends BaseTest {
boolean serviceIsOnline() {
// Test connection, etc.
return true
}
}
class SkipTest extends BaseTest {
boolean serviceIsOnline() {
return false
}
}
Documentation
instance
The specification instance, if instance fields, shared
fields, or instance methods are needed. If this property is used, the
whole annotated element cannot be skipped up-front without executing
fixtures, data providers and similar. Instead, the whole workflow is
followed up to the feature method invocation, where then the closure
is checked, and it is decided whether to abort the specific iteration
or not.
As an extra, another way you can programmatically skip a test is using the where label:
class MyTest extends Specification {
List getAvailableServices() {
// You can test connections here or your conditions
// to enable testing or not.
return available
}
#Unroll
def "Testing something"() {
setup:
URL url = serviceUrl.toURL()
expect:
assert url.text.contains("Hello")
where:
serviceUrl << availableServices
}
}

Related

Grails Model Unity Test

I have a "domain"(model) that want to do an Unity Test to check if works.
But when I execute the test I get
java.lang.IllegalArgumentException: Test class can only have one constructor
Thats always happen when trying to initialize a class of some domain(model).
What would be the approach to do correctly the testcase?
class Race {
static constraints = { }
String name
Date startDate
String city
String state
BigDecimal distance
BigDecimal cost
Integer maxRunners = 100000
BigDecimal inMiles() {
return 0.6
}
}
And in the Unity Test Class
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(Race)
class RaceTest extends Specification {
void testInMiles() {
when:
def model = new Race(distance:5.0);
then:
0.6 == model.inMiles()
}
}
In Grails 2.4.x (which is what I'm assuming you're using) the default test type is a Spock test, and that's what's created by the generate-* scripts. You can still write your own tests in JUnit 3 or 4 style if you prefer. But test classes in Spock (at least using the Grails integration, I'm not sure if it's as strict outside of Grails) have to have names ending in "Spec". That's why you're seeing that error.
Test methods do not have to be void and start with "test" (JUnit 3 style) or be void and have an #Test annotation (JUnit 4 style). The test runner decides if a method is a test method if it's public (either explicitly or if there's no scope modifier) and there's at least one labelled block, e.g. when:, given:, then:, etc. Further, Spock uses some AST magic to allow you to use spaces in method names (you just have to quote the whole name) and have expressive, self-descriptive method names, e.g.
def 'an admin without ROLE_SUPER cannot view records of other admins'() {
...
}

Do GORM hook methods work in Grails integration test

I have simple question
Do GORM hooks method (beforeUpdate, afterLoad etc) work during integration tests? Are they evaluated?
I have integration test (my test class extends GroovyTestCase) and as I can see changes that I make in afterLoad method on my domain object doesn't seem to work (domain object stays the same after loading) and when I test it manually (run my app) it works (domain object is changed successfully). Also I cannot see any logs or prints to console from hook methods during integration tests.
Is this expected behavior or I am missing something?
Here is some code:
I have PackageState domain object which has field accountID (Integer).
In PackageState I have afterLoad hook:
def afterLoad() {
this.accountID = 33333
}
My test suite looks something like this :
void "test3"() {
PackageState packageState1 = PackageState.findByAccountID(11111)
assertEquals(33333, packageState1.accountID)
}
Message is :
junit.framework.AssertionFailedError: expected:<33333> but was:<11111>
So seems to me that hook is not called :/ ...
Thanks,
Ivan
You can integration test those event hooks by surrounding the dynamic method (or any other GORM method) by withNewSession as:
class PackageStateSpec extends IntegrationSpec {
PackageState packageState1
void "test something"() {
given:
PackageState.withNewSession{
packageState1 = PackageState.findByAccountID(11111)
}
expect:
packageState1.accountID == 33333
}
}
Events will be registered with a new session every time when tested from integration tests.

Grails integration test - domain object equality

Setting up some integration tests, I'm having issues with domain class equality. The equality works as expected during normal execution, but when testing the Service methods through an integration test, the test for equality is coming back false.
One service (called in the setUp() of the Test Case) puts a Domain object into the session
SomeService {
setSessionVehicle(String name) {
Vehicle vehicle = Vehicle.findByName(name)
session.setAttribute("SessionVehicle", vehicle)
}
getSessionVehicle() {
return session.getAttribute("SessionVehicle")
}
}
Elsewhere in another service, I load an object and make sure the associated attribute object matches the session value:
OtherService {
getEngine(long id) {
Vehicle current = session.getAttribute("SessionVehicle")
Engine engine = Engine.get(id)
if(!engine.vehicle.equals(current)) throw Exception("Blah blah")
}
}
This works as expected during normal operation, preventing from loading the wrong engine (Ok, I sanitized the class names, pretend it makes sense). But in the integration test, that .equals() fails when it should succeed:
Vehicle testVehicle
setUp() {
Vehicle v = new Vehicle("Default")
v.save()
someService.setSessionVehicle("Default")
testVehicle = someService.getSessionVehicle()
}
testGetEngine() {
List<Engine> engines = Engine.findAllByVehicle(testVehicle)
//some assertions and checks
Engine e = otherService.getEngine(engines.get(0).id)
}
The findAll() call is correctly returning the list of all Engines associated with the vehicle in the session, but when I try to look up an individual Engine by ID, the equality check for session Vehicle vs Vehicle on the found Engine fails. Only a single vehicle has been created at this point and the Exception message displays that the session Vehicle and the Engine.Vehicle exist and are the same value.
If I attempt this equality check in the testCase itself, it fails, but I'm able to change the testCase to check if(vehicle.id == sessionVehicle.id) which succeeds, but I'm not keen on changing my production code in order to satisfy an integration test.
Am I doing something wrong when setting up these domain objects in my test case that I should be doing differently?
First of all, the equality check you are doing is just checking the reference. You should not use the default equals method for your check, better override the equals method in domain class.
There are two ways you can override the equals method:
1) you can use your IDE to auto-generate code for equals method(a lot of null checking etc..).
2) Preferred way: You can use EqualsBuilder and HashCodeBuilder classes from the Apache Commons project. The library should be already available to your application, or download the JAR file and place in lib. Here is sample code for using EqualsBuilder:
boolean equals(o) {
if ( !(o instanceof Vehicle) ) {
return false
}
def eb = new EqualsBuilder()
eb.append(id, o.id)
eb.append(name, o.name)
eb.append(otherProperties, o.otherProperties)
....
return eb.isEquals()
}
Another point is: how you are getting session in service? from RequestContextHolder? Its a good practice to not access session directly from service, rather send the value as method parameter in the service.

Scalatest sharing service instance across multiple test suites

I have FlatSpec test classes which need to make use of a REST service for some fixture data. When running all the tests in one go I only really want to instantiate the REST client once as it may be quite expensive. How can I go about this and can I also get it to work for running just one test class when I am running in my IDE?
1. Use mocking:
I would advice you to use some kind of mocking when you try to test REST service. You can try for example scala-mock. Creation of mock service isn't time/cpu consuming, so you can create mocks in all your tests and don't need to share them.
Look:
trait MyRestService {
def get(): String
}
class MyOtherService(val myRestService: MyRestService) {
def addParentheses = s"""(${myRestService.getClass()})"""
}
import org.scalamock.scalatest.MockFactory
class MySpec extends FreeSpec with MockFactory {
"test1 " in {
// create mock rest service and define it's behaviour
val myRestService = mock[MyRestService]
val myOtherService = new MyOtherService(myRestService)
inAnyOrder {
(myRestService.get _).expects().returning("ABC")
}
//now it's ready, you can use it
assert(myOtherService.addParentheses === "(ABC)")
}
}
2. Or use Sharing fixtures:
If you still want to use real implementation of you REST service and create only one instance and then share it with some test condider using:
get-fixture methods => Use it when you need the same mutable fixture objects in multiple tests, and don't need to clean up after.
withFixture(OneArgTest) => Use when all or most tests need the same fixtures that must be cleaned up afterwords.
Refer to http://www.scalatest.org/user_guide/sharing_fixtures#loanFixtureMethods for more details and code examples.
If you want to share the same fixture against multiple Suites use org.scalatest.Suites and #DoNotDiscover annotation (these requires at least scalatest-2.0.RC1)
Pawel's last comment fits well.
It was easier by inheriting from Suite with BeforaAndAfterAll instead of Suites.
import com.typesafe.config.ConfigFactory
import com.google.inject.Guice
import org.scalatest.{BeforeAndAfterAll, Suite}
import net.codingwell.scalaguice.InjectorExtensions.ScalaInjector
class EndToEndSuite extends Suite with BeforeAndAfterAll {
private val injector = {
val config = ConfigFactory.load
val module = new AppModule(config) // your module here
new ScalaInjector(Guice.createInjector(module))
}
override def afterAll {
// your shutdown if needed
}
override val nestedSuites = collection.immutable.IndexedSeq(
injector.instance[YourTest1],
injector.instance[YourTest2] //...
)
}

Testing Grails taglibs that call other taglibs

Say I've got two taglibs, Foo which does something specific for a particular part of my application, and Util which is shared across the whole thing. I want to do something like this:
class UtilTagLib {
def utilTag = { attrs ->
...
}
}
class FooTagLib {
def fooTag = {
...
out << g.utilTag(att1: "att1", att2: "att2")
...
}
}
However, when I do this, and try to run my unit test for fooTag(), I get:
groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.grails.web.pages.GroovyPage.utilTag() is applicable for argument types: (java.util.LinkedHashMap) values: [[att1:att1, att2:att2]]
I tried giving UtilTagLib its own namespace
static namespace = "util"
and changing the call to
out << util.utilTag(...)
but this just gets me
groovy.lang.MissingPropertyException: No such property: util for class: org.example.FooTagLib
Possibly also of note: In the log, I see:
WARN - Bean named 'groovyPagesUriService' is missing.
Obviously, UtilTagLib isn't getting created and injected correctly. How can I fix this?
Solution: add the call
mockTagLib UtilTagLib
to the setUp() (or #Before) method of the test case. This is a method on GroovyPageUnitTestMixin that, somewhat counterintuitively, instantiates the specified tag library -- the real one, not a mock -- and wires it into the Grails application context. It's used internally to set up the actual taglib under test (in this case FooTagLib), but it also works to set up additional collaborator tag libs.
Note that this isn't perfect, since it makes it more of an integration test than a pure unit test -- ideally we would be using a mock UtilTagLib and just testing the interaction.
One approach would be to refactor the line:
out << g.utilTag(att1: "att1", att2: "att2")
in to its own method, say "renderUtilTag(...)", then mock that out in the unit test, e.g.:
FooTagLib.metaClass.renderUtilTag = { /* something */ }
That way you're testing the functionality of FooTagLib only in the unit test, with no dependency on UtilTagLib.

Resources