Check the url in a Geb/Spock test - groovy

In my Spock functional test, I want to test that the current location of the browser is not a certain url. I know I can do:
try {
at(PageWithUrlIDontWant)
// do something
} catch (AssertionError) {
// pass because this is the common branch
}
However, this is using exceptions to control branching. Is there a way, besides at, to check what url the browser is at? I have tried page.pageUrl and browser.pageUrl but they are both null.

As of Geb 0.7.0, there is a new isAt() method on the class Browser. GebSpec proxies methodMissing calls to it's Browser instance so this method is available in your test. This method returns true/false instead of throwing an exception and should be exposed.
Example:
def "navigating to GoodPage doesn't up at BadPage"() {
when:
to GoodPage
then:
!isAt(BadPage)
}

Related

How to declare a constructor or extends a class of a groovy script?

I am working on a shared library for Jenkins, and I want to access some utilities methods between some classes, but not all of them, thus I have established some statements:
I would like to avoid using static methods, since it does not access pipeline steps directly, and passing the pipeline instance every call would be a pain;
I would like to avoid a singleton as well, or prefixing every method call with the util class' instance;
Since it is not supposed to be shared between all classes I would like to avoid putting every method as a file on vars/ special directory, but I would like a similar behavior;
Despite extending the class would be a anti-pattern, it would be acceptable, though I would like to avoid the verbose Java syntax for declaring the class the same name as the file, once it is implicit in groovy;
This question does solve my problem partially, although there are issues with serialization, I noted that when I use checkpoint and some build is resumed from some stage, the instance loses all extra methods.
This other question would have helped me fix the serialization issue, however the author seems the have solved the root cause of his problem using a way that is not the original question titled for.
Is there a way to extends a implicit script class in groovy without using the class NameOfFile extends SomeOtherClass { put every thing inside this block } syntax? And without working with inner-class?
Or else, is there a way to declare a constructor using the script groovy syntax analogue as the previous question?
Or even, is there a way to change the serialization behavior to install the extra methods again after unserializing?
Appendix
The script syntax works more-or-less like this:
Consider the content of file src/cicd/pipeline/SomePipeline.groovy:
package cicd.pipeline
// there is no need to wrap everything inside class SomePipeline,
// since it is implicit
def method() {
// instance method, here I can access pipeline steps freely
}
def static otherMethod() {
// static method, here it is unable to access pipeline steps
// without a instance
}
#groovy.transform.Field
def field
def call() {
// if the class is used as method it will run
this.method()
SomePipeline.otherMethod() // or simply otherMethod() should work
this.field = 'foo'
println "this instance ${this.getClass().canonicalName} should be cicd.pipeline.SomePipeline"
}
// any code other than methods or variables with #Field
// annotation will be inside a implicit run method that is
// triggered likewise main method but isn't a static one
def localVar = 'foo'
println "It will not execute on constructor since it is on run: $localVar"
println "Method: ${org.codehaus.groovy.runtime.StackTraceUtils.sanitize(new Throwable()).stackTrace[0].methodName}"
println "this instance ${this.getClass().canonicalName} should be cicd.pipeline.SomePipeline"
If I was going to use the Java verbose syntax I would have to wrap almost everything inside a class SomePipeline which is implicit in groovy, this is the script syntax I want to keep.
I realised that this.getClass().superclass.canonicalName when outside Jenkins pipeline is groovy.lang.Script and when inside pipeline is org.jenkinsci.plugins.workflow.cps.CpsScript and based on this resource I was able to elaborate the following solution:
abstract class CustomScript extends org.jenkinsci.plugins.workflow.cps.CpsScript {
public CustomScript() {
// do something here, it will always execute regardless
// serialization, and before everything
}
}
#groovy.transform.BaseScript CustomScript baseScript
That is it, worked as expected! Of course you can elaborate this solution better in order to reduce repeating and avoid inner-classes, but I will leave it for your imagination.

In case of multiple when and then blocks is it possible to continue running another block if one fails?

I have a test class:
class Homepage extends AbstractTest {
#TestCase('TC44424')
def "Rust Checkout - Homepage gallery blade"() {
given:
code....
when:
code..
then:
code..
when:
code..
then:
code..
}
}
So here if suppose my first When-Then block fails I don't want my script to fail I want it to go to another When-Then block and continue running.Is it possible?
Since these test cases aren't building up any state (and thus you think they could continue running after a previous one fails), you might consider putting each when/then in their own test method. You can move the given to a def setup() { ... } method as well.
Alternatively, you could construct your test with a where block. This might not be feasible depending on how different each test case is from each other.

Mockito isNotNull passes null

Thanks in advance for the help -
I am new to mockito but have spent the last day looking at examples and the documentation but haven't been able to find a solution to my problem, so hopefully this is not too dumb of a question.
I want to verify that deleteLogs() calls deleteLog(Path) NUM_LOGS_TO_DELETE number of times, per path marked for delete. I don't care what the path is in the mock (since I don't want to go to the file system, cluster, etc. for the test) so I verify that deleteLog was called NUM_LOGS_TO_DELETE times with any non-null Path as a parameter. When I step through the execution however, deleteLog gets passed a null argument - this results in a NullPointerException (based on the behavior of the code I inherited).
Maybe I am doing something wrong, but verify and the use of isNotNull seems pretty straight forward...here is my code:
MonitoringController mockController = mock(MonitoringController.class);
// Call the function whose behavior I want to verify
mockController.deleteLogs();
// Verify that mockController called deleteLog the appropriate number of times
verify(mockController, Mockito.times(NUM_LOGS_TO_DELETE)).deleteLog(isNotNull(Path.class));
Thanks again
I've never used isNotNull for arguments so I can't really say what's going wrong with you code - I always use an ArgumentCaptor. Basically you tell it what type of arguments to look for, it captures them, and then after the call you can assert the values you were looking for. Give the below code a try:
ArgumentCaptor<Path> pathCaptor = ArgumentCaptor.forClass(Path.class);
verify(mockController, Mockito.times(NUM_LOGS_TO_DELETE)).deleteLog(pathCaptor.capture());
for (Path path : pathCaptor.getAllValues()) {
assertNotNull(path);
}
As it turns out, isNotNull is a method that returns null, and that's deliberate. Mockito matchers work via side effects, so it's more-or-less expected for all matchers to return dummy values like null or 0 and instead record their expectations on a stack within the Mockito framework.
The unexpected part of this is that your MonitoringController.deleteLog is actually calling your code, rather than calling Mockito's verification code. Typically this happens because deleteLog is final: Mockito works through subclasses (actually dynamic proxies), and because final prohibits subclassing, the compiler basically skips the virtual method lookup and inlines a call directly to the implementation instead of Mockito's mock. Double-check that methods you're trying to stub or verify are not final, because you're counting on them not behaving as final in your test.
It's almost never correct to call a method on a mock directly in your test; if this is a MonitoringControllerTest, you should be using a real MonitoringController and mocking its dependencies. I hope your mockController.deleteLogs() is just meant to stand in for your actual test code, where you exercise some other component that depends on and interacts with MonitoringController.
Most tests don't need mocking at all. Let's say you have this class:
class MonitoringController {
private List<Log> logs = new ArrayList<>();
public void deleteLogs() {
logs.clear();
}
public int getLogCount() {
return logs.size();
}
}
Then this would be a valid test that doesn't use Mockito:
#Test public void deleteLogsShouldReturnZeroLogCount() {
MonitoringController controllerUnderTest = new MonitoringController();
controllerUnderTest.logSomeStuff(); // presumably you've tested elsewhere
// that this works
controllerUnderTest.deleteLogs();
assertEquals(0, controllerUnderTest.getLogCount());
}
But your monitoring controller could also look like this:
class MonitoringController {
private final LogRepository logRepository;
public MonitoringController(LogRepository logRepository) {
// By passing in your dependency, you have made the creator of your class
// responsible. This is called "Inversion-of-Control" (IoC), and is a key
// tenet of dependency injection.
this.logRepository = logRepository;
}
public void deleteLogs() {
logRepository.delete(RecordMatcher.ALL);
}
public int getLogCount() {
return logRepository.count(RecordMatcher.ALL);
}
}
Suddenly it may not be so easy to test your code, because it doesn't keep state of its own. To use the same test as the above one, you would need a working LogRepository. You could write a FakeLogRepository that keeps things in memory, which is a great strategy, or you could use Mockito to make a mock for you:
#Test public void deleteLogsShouldCallRepositoryDelete() {
LogRepository mockLogRepository = Mockito.mock(LogRepository.class);
MonitoringController controllerUnderTest =
new MonitoringController(mockLogRepository);
controllerUnderTest.deleteLogs();
// Now you can check that your REAL MonitoringController calls
// the right method on your MOCK dependency.
Mockito.verify(mockLogRepository).delete(Mockito.eq(RecordMatcher.ALL));
}
This shows some of the benefits and limitations of Mockito:
You don't need the implementation to keep state any more. You don't even need getLogCount to exist.
You can also skip creating the logs, because you're testing the interaction, not the state.
You're more tightly-bound to the implementation of MonitoringController: You can't simply test that it's holding to its general contract.
Mockito can stub individual interactions, but getting them consistent is hard. If you want your LogRepository.count to return 2 until you call delete, then return 0, that would be difficult to express in Mockito. This is why it may make sense to write fake implementations to represent stateful objects and leave Mockito mocks for stateless service interfaces.

Can't pass GET parameter while unit testing Zend Framework 2

I'm having hard time trying to unit test (phpUnit) one of my modules in ZF2. What I'm trying to do is determine whether a classname is present on one of the elements on page when a GET parameter is passed to the controller.
It all works from the browser, however I can't get the GET parameter to be recognized at all when trying to unit test.
This is my code for unit testing:
<?php
namespace ComponentManager\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
class ComponentManagerControllerTest extends AbstractHttpControllerTestCase
{
public function setUp()
{
$this->setApplicationConfig(
include 'config/application.config.php'
);
parent::setUp();
}
public function testAdminComponentCodeCanBeAccessed()
{
$this->dispatch('/ComponentManager/requestComponent/product/details-1/details-1', 'GET', array('admin' => 1));
// I also tried: $this->dispatch('/ComponentManager/requestComponent/product/details-1/details-1?admin=1');
$this->assertResponseStatusCode(200);
$this->assertMatchedRouteName('ComponentManager/path');
$this->assertControllerName('ComponentManager\Controller\ComponentManager');
$this->assertControllerClass('ComponentManagerController');
$this->assertActionName('requestComponent');
$this->assertModuleName('ComponentManager');
// test will fail here
$this->assertQuery('div.config-active-wrapper');
}
}
The "div.config-active-wrapper" selector works fine when I remove the check for admin parameter presence in GET but when I re-add it, the GET parameter doesn't get recognised at all. Any ideas?
The problem here was that unit testing is a CLI operation and no superglobals are being populated while in CLI. Simple and stupid :P
A solution is to not use superglobals like $_GET here but to pass this "admin" parameter via some ACL and a controller instead.

Geb drive method

I'm trying to learn how to use Geb and I'm getting an error. Could you guys help me out?
I'm trying to use the drive method but it is not working. I've tested a few of the other Browser's methods and they work all right. Just the drive method is giving me trouble.
I've checked the API and googled around but didn't find anything helpful. The strange thing is the fact that I don't get an error message. There is no Exception. I'm running the code on Groovy's console and Firefox just chills for a while and then the execution finishes.
Geb 0.9.2, FirefoxDriver and JDK 7
import org.openqa.selenium.WebDriver;
import geb.Browser
import org.openqa.selenium.firefox.FirefoxDriver
public class MyTest {
Browser browser;
void test(){
browser = new Browser(driver: new FirefoxDriver())
browser.go "http://www.google.com" // this works
browser.$("div button", name: "btnK").text() == "Google Search" // this works
browser.drive { // WHY U NO WORK?!!
go "http://www.google.com"
}
}
}
x = MyTest()
x.test()
You should know that drive() is a static method and it's designed to be used in scripts where you don't instantiate a browser instance. You have to decide - you either use a browser instance or the Browser.drive {} method. Yo cannot do both.
You might also consider using one of the integrations with testing frameworks - by doing so you'll get Geb to manage a browser instance for you.

Resources