As all who have used it know Selenium is a powerful tool when testing front-end applications. I personally use it in combination with protractor. This is because most of the work I do is with Angular and AngularJS applications.
When you are using Typescript extending classes is an easy thing. In light of this I’ve been experimenting with new approaches to creating page objects. Continue reading →
As someone who spends quite some time writing and checking unit and e2e tests I’ve started noticing a trend I’m somewhat confused by. There have been multiple occasions in which I’ve encountered test logic (repeatable and single use) in either test specifications or page objects.
So I decided to share my approach to writing and foremost separating my test code into three categories. Those being: Specifications , Sequences and Page Objects.
I’d like to start with the following service announcement,
you really shouldn’t need this.
That being said; I’ve started using a third party component library which led to a use-case for this.
It so happened that i was creating a page object for a component library called ag-grid. At first glance i thought nothing special about it; and that i could retrieve rows normally. ag-grid however supports the pinning of columns to the left and/or right side of the list. And these rows then fall inside their own respective markups.
Now you can easily create a structure allowing me to target the (non-)pinned columns. But for ease of use being able to get full set of cells can also be useful and might be more suited for the test case.
As you can see it returns a new ElementArrayFinder which is the return type for the all locator and thus what we need for the aggregate.
In order to create the aggregate response it resolves each locator’s getWebElements response through Promise.all. It then fulfils the deferredAggregate with the promised results reduced to a single WebElement array.
In this implementation the combinedLocator.toString is also supplied. This is to ensure that feedback for this aggregation also contains all locators in the message. For the creation of the ElementArrayFinder itself it is not required though
Creating and working with mocks and stubs in Spock is easy. If we want to interact with our mocks and stubs we have several options to return values. One of the options is the triple right shift operator >>>. With this operator we can define different return values for multiple invocations of the stubbed or mocked method. For example we can use the >>> operator followed by a list of return values ['abc', 'def', 'ghi']. On the first invocation abc is return, the second invocation returns def and the third (and following) invocation(s) return ghi.
In a previous blog post we have seen the IgnoreIf extension. There is also a counterpart: the Requires extension. If we apply this extension to a feature method or specification class than the method or whole class is executed when the condition for the @Requires annotation is true. If the condition is false the method or specification is not executed. As a value for the @Requires annotation we must specify a closure. In the closure Spock adds some properties we can use for our conditions:
jvm can be used to check a Java version or compatibility.
sys returns the Java system properties.
env used to access environment variables.
os can be used to check for operating system names.
javaVersion has the Java version as BigDecimal, eg. 1.8.
If we need to add a Java system property or change the value of a Java system property inside our specification, then the change is kept as long as the JVM is running. We can make sure that changes to Java system properties are restored after a feature method has run. Spock offers the RestoreSystemProperties extension that saves the current Java system properties before a method is run and restores the values after the method is finished. We use the extension with the @RestoreSystemProperties annotation. The annotation can be applied at specification level or per feature method.
Spock is able to change the execution order of test methods in a specification. We can tell Spock to re-run failing methods before successful methods. And if we have multiple failing or successful tests, than first run the fastest methods, followed by the slower methods. This way when we re-run the specification we immediately see the failing methods and could stop the execution and fix the errors. We must set the property optimizeRunOrder in the runner configuration of the Spock configuration file. A Spock configuration file with the name SpockConfig.groovy can be placed in the classpath of our test execution or in our USER_HOME/.spock directory. We can also use the Java system property spock.configuration and assign the filename of our Spock configuration file.
In a previous post we saw how can use the Spock configuration file to include or exclude specifications based on annotations. Instead of using annotations we can also use classes to include or exclude specifications. For example we could have a base specification class DatabaseSpecification. Other specifications dealing with databases extend this class. To include all these specifications we use the DatabaseSpecification as value for the include property for the test runner configuration.
Because Java (and Groovy) doesn’t support real multiple inheritance this might be a problem if we already have specifications that extends a base class, but the base class cannot be used as filter for the include and exclude runner configuration. Luckily we can also use an interface as the value for the inclusion or exclusion. So we could simple create a marker interface and implement this interface for these specifications we want to include or exclude from the test execution.
One of the lesser known and documented features of Spock if the external Spock configuration file. In this file we can for example specify which specifications to include or exclude from a test run. We can specify a class name (for example a base specification class, like DatabaseSpec) or an annotation. In this post we see how to use annotations to have some specifications run and others not.
One of the biggest struggles while testing web applications that use AJAX is dealing with the timing issues caused by asynchronous nature of the request. A common solution is using timeouts to wait for the AJAX call to be completed. This could cause lots of timing issues. Using Geb there is a better way to determine that the AJAX call and its callback has been completed.