How to test logic in constructor? - spock

My test class like this:
public class HandlerTest extends Specification {
Handler hander
EventBus eventBus=Mock()
def setup(){
handler=new Handler(eventBus)
}
def "constructor"(){
//How to verify two events do get added to eventBus?
}
}
and Constructor of Handler(it is a java class)
public Handler(EventBus eventBus)
{
eventBus.add(FetchSearchWordsEvent.TYPE, this);
eventBus.add(SetSearchBoxTextEvent.TYPE, this);
}
Question is:
how to verify that two events do get registered?

I would move the call to Handler constructor into the test itself given that it is the function under test.
Try the following:
public class HandlerTest extends Specification {
Handler hander
def mockEventBus = Mock(EventBus)
def "constructor"(){
when:
new Handler(mockEventBus)
then:
1 * mockEventBus.add(FetchSearchWordsEvent.TYPE, _ as Handler)
1 * mockEventBus.add(SetSearchBoxTextEvent.TYPE, _ as Handler)
}
}
The functionality of EventBus.add() should be tested separately.

It depends on how registerHandler is implemented, and what exactly you want to verify. If the goal is to verify that the constructor ultimately calls some methods on eventBus, you can just use regular mocking. If the goal is to verify that the constructor calls registerHandler on itself, you can use partial mocking using Spy(), as explained in the Spock reference documentation.
PS: Note that partial mocking is considered a smell. Often it's better to change the class under test to make it easier to test. For example, you could add a method that allows to query which handlers have been registered. Then you won't need mocking at all.

Related

Add custom information to Spock Global Extension

I have configured Spock Global Extension and static class ErrorListener inside it. Works fine for test errors when I want to catch feature title and errors if they happen. But how can I add some custom information to the listener?
For example I have test that calls some API. In case it fails I want to add request/response body to the listener (and report it later). Obviously I have request/response inside the feature or I can get it. How can I pass this information to the Listener and read later in the handling code?
package org.example
import groovy.json.JsonSlurper
import org.spockframework.runtime.AbstractRunListener
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo
import org.spockframework.runtime.model.SpecInfo
import spock.lang.Specification
class OpenBrewerySpec extends Specification{
def getBreweryTest(){
def breweryText = new URL('https://api.openbrewerydb.org/breweries/1').text
def breweryJson = new JsonSlurper().parseText(breweryText)
//TODO catch breweryText for test result reporting if it is possible
expect:
breweryJson.country == 'United States'
}
def cleanup() {
specificationContext.currentSpec.listeners
.findAll { it instanceof TestResultExtension.ErrorListener }
.each {
def errorInfo = (it as TestResultExtension.ErrorListener).errorInfo
if (errorInfo)
println "Test failure in feature '${specificationContext.currentIteration.name}', " +
"exception class ${errorInfo.exception.class.simpleName}"
else
println "Test passed in feature '${specificationContext.currentIteration.name}'"
}
}
}
class TestResultExtension extends AbstractGlobalExtension {
#Override
void visitSpec(SpecInfo spec) {
spec.addListener(new ErrorListener())
}
static class ErrorListener extends AbstractRunListener {
ErrorInfo errorInfo
#Override
void beforeIteration(IterationInfo iteration) {
errorInfo = null
}
#Override
void error(ErrorInfo error) {
errorInfo = error
}
}
}
Create file src/test/resources/META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
and place string "org.example.TestResultExtension" there to enable extension.
I am pretty sure you found my solution here. Then you also know that it is designed to know in a cleanup() methods if the test succeeded or failed because otherwise Spock does not make the information available. I do not understand why deliberately omitted that information and posted a fragment instead of the whole method or at least mentioned where your code snippet gets executed. That is not a helpful way of asking a question. Nobody would know except for me because I am the author of this global extension.
So now after having established that you are inside a cleanup() method, I can tell you: The information does not belong into the global extension because in the cleanup() method you have access to information from the test such as fields. Why don't you design your test in such a way that whatever information cleanup() needs it stored in a field as you would normally do without using any global extensions? The latter is only meant to help you establish the error status (passed vs. failed) as such.
BTW, I even doubt if you need additional information in the cleanup() method at all because its purpose it cleaning up, not reporting or logging anything. For that Spock has a reporting system which you can also write extensions for.
Sorry for not being more specific in my answer, but your question is equally unspecific. It is an instance of the XY problem, explaining how you think you should do something instead of explaining what you want to achieve. Your sample code omits important details, e.g. the core test code as such.

What is the purpose of the getter methods in Components in Dagger 2?

I am trying to understand Components in Dagger 2. Here is an example:
#Component(modules = { MyModule.class })
public interface MyComponent {
void inject(InjectionSite injectionSite);
Foo foo();
Bar bar();
}
I understand what the void inject() methods do. But I don't understand what the other Foo foo() getter methods do. What is the purpose of these other methods?
Usage in dependent components
In the context of a hierarchy of dependent components, such as in this example, provision methods such as Foo foo() are for exposing bindings to a dependent component. "Expose" means "make available" or even "publish". Note that the name of the method itself is actually irrelevant. Some programmers choose to name these methods Foo exposeFoo() to make the method name reflect its purpose.
Explanation:
When you write a component in Dagger 2, you group together modules containing #Provides methods. These #Provides methods can be thought of as "bindings" in that they associate an abstraction (e.g., a type) with a concrete way of resolving that type. With that in mind, the Foo foo() methods make the Component able to expose its binding for Foo to dependent components.
Example:
Let's say Foo is an application Singleton and we want to use it as a dependency for instances of DependsOnFoo but inside a component with narrower scope. If we write a naive #Provides method inside one of the modules of MyDependentComponent then we will get a new instance. Instead, we can write this:
#PerFragment
#Component(dependencies = {MyComponent.class }
modules = { MyDependentModule.class })
public class MyDependentComponent {
void inject(MyFragment frag);
}
And the module:
#Module
public class MyDepedentModule {
#Provides
#PerFragment
DependsOnFoo dependsOnFoo(Foo foo) {
return new DependsOnFoo(foo);
}
}
Assume also that the injection site for DependentComponent contains DependsOnFoo:
public class MyFragment extends Fragment {
#Inject DependsOnFoo dependsOnFoo
}
Note that MyDependentComponent only knows about the module MyDependentModule. Through that module, it knows it can provide DependsOnFoo using an instance of Foo, but it doesn't know how to provide Foo by itself. This happens despite MyDependentComponent being a dependent component of MyComponent. The Foo foo() method in MyComponent allows the dependent component MyDependentComponent to use MyComponent's binding for Foo to inject DependsOnFoo. Without this Foo foo() method, the compilation will fail.
Usage to resolve a binding
Let's say we would like to obtain instances of Foo without having to call inject(this). The Foo foo() method inside the component will allow this much the same way you can call getInstance() with Guice's Injector or Castle Windsor's Resolve. The illustration is as below:
public void fooConsumer() {
DaggerMyComponent component = DaggerMyComponent.builder.build();
Foo foo = component.foo();
}
Dagger is a way of wiring up graphs of objects and their dependencies. As an alternative to calling constructors directly, you obtain instances by requesting them from Dagger, or by supplying an object that you'd like to have injected with Dagger-created instances.
Let's make a coffee shop, that depends on a Provider<Coffee> and a CashRegister. Assume that you have those wired up within a module (maybe to LightRoastCoffee and DefaultCashRegister implementations).
public class CoffeeShop {
private final Provider<Coffee> coffeeProvider;
private final CashRegister register;
#Inject
public CoffeeShop(Provider<Coffee> coffeeProvider, CashRegister register) {
this.coffeeProvider = coffeeProvider;
this.register = register;
}
public void serve(Person person) {
cashRegister.takeMoneyFrom(person);
person.accept(coffeeProvider.get());
}
}
Now you need to get an instance of that CoffeeShop, but it only has a two-parameter constructor with its dependencies. So how do you do that? Simple: You tell Dagger to make a factory method available on the Component instance it generates.
#Component(modules = {/* ... */})
public interface CoffeeShopComponent {
CoffeeShop getCoffeeShop();
void inject(CoffeeService serviceToInject); // to be discussed below
}
When you call getCoffeeShop, Dagger creates the Provider<Coffee> to supply LightRoastCoffee, creates the DefaultCashRegister, supplies them to the Coffeeshop constructor, and returns you the result. Congratulations, you are the proud owner of a fully-wired-up coffeeshop.
Now, all of this is an alternative to void injection methods, which take an already-created instance and inject into it:
public class CoffeeService extends SomeFrameworkService {
#Inject CoffeeShop coffeeShop;
#Override public void initialize() {
// Before injection, your coffeeShop field is null.
DaggerCoffeeShopComponent.create().inject(this);
// Dagger inspects CoffeeService at compile time, so at runtime it can reach
// in and set the fields.
}
#Override public void alternativeInitialize() {
// The above is equivalent to this, though:
coffeeShop = DaggerCoffeeShopComponent.create().getCoffeeShop();
}
}
So, there you have it: Two different styles, both of which give you access to fully-injected graphs of objects without listing or caring about exactly which dependencies they need. You can prefer one or the other, or prefer factory methods for the top-level and members injection for Android or Service use-cases, or any other sort of mix and match.
(Note: Beyond their use as entry points into your object graph, no-arg getters known as provision methods are also useful for exposing bindings for component dependencies, as David Rawson describes in the other answer.)

Adding Dagger to an existing project

I'm trying to add Dagger to an existing web application and am running into a design problem.
Currently our Handlers are created in a dispatcher with something like
registerHandler('/login', new LoginHandler(), HttpMethod.POST)
Inside the login handler we might call a function like
Services.loginService.login('username', 'password');
I want to be able to inject the loginService into the handler, but am having trouble figuring out the best approach. There is a really long list of handlers in the dispatcher, and injecting them all as instance variables seems like a large addition of code.
Is there a solution to this type of problem?
Based on your comment about having different services to inject. I would propose next solution.
ServicesProvider:
#Module(injects = {LoginHandler.class, LogoutHandler.class})
public class ServicesProvider {
#Provides #Singleton public LoginService getLoginService() {
return new LoginService();
}
}
LoginHandler.java:
public class LoginHandler extends Handler {
#Inject LoginService loginService;
}
HttpNetwork.java
public class HttpNetwork extends Network {
private ObjectGraph objectGraph = ObjectGraph.create(new ServicesProvider());
public registerHandler(String path, Handler handler, String methodType) {
getObjectGraph().inject(handler);
}
}
There is one week point in this solution - you can't easily change ServiceProvider for test purpose (or any other kind of purpose). But if you inject it also (for example with another object graph or just through constructor) you can fix this situation.

Groovy method interception

In my Grails app I've installed the Quartz plugin. I want to intercept calls to every Quartz job class' execute method in order to do something before the execute method is invoked (similar to AOP before advice).
Currently, I'm trying to do this interception from the doWithDynamicMethods closure of another plugin as shown below:
def doWithDynamicMethods = { ctx ->
// get all the job classes
application.getArtefacts("Job").each { klass ->
MetaClass jobMetaClass = klass.clazz.metaClass
// intercept the methods of the job classes
jobMetaClass.invokeMethod = { String name, Object args ->
// do something before invoking the called method
if (name == "execute") {
println "this should happen before execute()"
}
// now call the method that was originally invoked
def validMethod = jobMetaClass.getMetaMethod(name, args)
if (validMethod != null) {
validMethod.invoke(delegate, args)
} else {
jobMetaClass.invokeMissingMethod(delegate, name, args)
}
}
}
}
So, given a job such as
class TestJob {
static triggers = {
simple repeatInterval: 5000l // execute job once in 5 seconds
}
def execute() {
"execute called"
}
}
It should print:
this should happen before execute()
execute called
But my attempt at method interception seems to have no effect and instead it just prints:
execute called
Perhaps the cause of the problem is this Groovy bug? Even though the Job classes don't explicitly implement the org.quartz.Job interface, I suspect that implicitly (due to some Groovy voodoo), they are instances of this interface.
If indeed this bug is the cause of my problem, is there another way that I can do "before method interception"?
Because all the job classes are Spring beans you can solve this problem using Spring AOP. Define an aspect such as the following (adjust the pointcut definition so that it matches only your job classes, I've assumed they are all in a package named org.example.job and have a class name that ends with Job).
#Aspect
class JobExecutionAspect {
#Pointcut("execution(public * org.example.job.*Job.execute(..))")
public void executeMethods() {}
#Around("executeMethods()")
def interceptJobExecuteMethod(ProceedingJoinPoint jp) {
// do your stuff that should happen before execute() here, if you need access
// to the job object call jp.getTarget()
// now call the job's execute() method
jp.proceed()
}
}
You'll need to register this aspect as a Spring bean (it doesn't matter what name you give the bean).
You can have your customized JobListener registered in the application to handle logics before execute() is triggered. You can use something like:-
public class MyJobListener implements JobListener {
public void jobToBeExecuted(JobExecutionContext context) {
println "Before calling Execute"
}
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) {}
public void jobExecutionVetoed(JobExecutionContext context) {}
}
Register the customized Job Listener to Quartz Scheduler in Bootstrap:-
Scheduler scheduler = ctx.getBean("quartzScheduler") //ctx being application context
scheduler.getListenerManager().addJobListener(myJobListener, allJobs())
resources.groovy:-
beans = {
myJobListener(MyJobListener)
}
One benefit I see here using this approach is that we don't need the second plugin used for method interception any more.
Second, we can register the listener to listen all jobs, specific jobs, and jobs in a group. Refer Customize Quartz JobListener and API for JobListener, TriggerListener, ScheduleListener for more insight.
Obviously, AOP is another approach if we do want want to use Quartz API.
You are not getting the job classes like that. If you refer to the Quartz plugin, you can get them by calling jobClasses:
application.jobClasses.each {GrailsJobClass tc -> ... }
see https://github.com/nebolsin/grails-quartz/blob/master/QuartzGrailsPlugin.groovy
If you actually look, you can see that they are almost doing what you are trying to acheive without the need to use aop or anything else.
For method interception implement invokeMethod on the metaclass. In my case the class was not of third party so I can modify the implementation.
Follow this blog for more information.

Using Spock to stub both Gorm and other methods in a Grails domain class

Sorry if this is a newbie question but I would really appreciate any insights the community could offer with regard to a problem I am having with stubbing the following method which I have in a Grails service, LocationService.
Location locate(String target, String locator, Application app, boolean sync = true) {
if (!target) throw new IllegalArgumentException("Illegal value for msid: " + target)
def locRequest = Request.create(target, Type.LOCATE)
if (!locRequest.save()) {
return Location.error(target, "Error persisting location request")
}
locationSource.locateTarget(target, locator, app, sync)
}
I have a domain class, Request, that as well as the default GORM methods also has some extra domain methods, eg. the create() method below
#EqualsAndHashCode
class Request {
String reference
String msid
Type type
Status status
Destination destination
DateTime dateCreated
DateTime dateCompleted
static create(String msid, Type type, Destination destination = Destination.DEFAULT) {
new Request(reference: reference(type), type: type, status: Status.INITIATED, dateCreated: new DateTime())
}
Finally, I have a Spock specification. I need to mock both the default GORM methods but also some stub some extra domain logic, eg, a static create method, in order to return a valid object to be persisted in the code under test.
Ideally, I would use Spock mocks but I can't use them here as according to the post below from Peter N, they need to be injected into the caller and in this case the Request (which I am trying to mock), is created as a local variable in the locate method in LocationService:
https://groups.google.com/forum/?fromgroups=#!topic/spockframework/JemiKvUiBdo
Nor can I use the Grails 2.x #Mock annotation as, although this will mock the GORM methods, I am unsure if i can mock/stub the additional static create() method from the Request class.
Hence, finally, I have been trying to use the Groovy StubFor / MockFor methods to do this as I believe that these will be used in the call to the test method by wrapping it in a use closure (as below).
Here is the test spec:
#TestFor(LocationService)
// #Mock(Request)
class LocationServiceSpec extends Specification {
#Shared app = "TEST_APP"
#Shared target = "123"
#Shared locator = "999"
def locationService = new LocationService()
LocationSource locationSource = Mock()
def "locating a valid target should default to locating a target synchronously"() {
given:
def stub = new StubFor(Request)
stub.demand.create { target, type -> new Request(msid: target, type: type) }
stub.demand.save { true }
1 * locationSource.locateTarget(target, locator, app, SYNC) >> { Location.create(target, point, cellId, lac) }
def location
when:
stub.use {
location = locationService.locate(target, locator, app)
}
then:
location
}
However, when I run the test, although the stubbed create method returns my Request stub object, I get a failure on the stubbed save method:
groovy.lang.MissingMethodException: No signature of method: com.domain.Request.save() is applicable for argument types: () values: []
Possible solutions: save(), save(boolean), save(java.util.Map), wait(), last(), any()
Could anybody please point out what I am doing wrong here or suggest the best approach to solve my particular case if needing to stub additional methods as well as GORM methods of a domain class that I can't inject directly into the code under test?
Thank you in advance,
Patrick
I believe you should be able to use Grails' #Mock annotation like you mentioned for the GORM methods, and then you will need to manually mock the static methods:
#TestFor(LocationService)
#Mock(Request)// This will mock the GORM methods, as you suggested
class LocationServiceSpec extends Specification {
...
void setup() {
Request.metaClass.static.create = { String msid, Type type, Destination destination = Destination.DEFAULT ->
//Some logic here
}
}
...
When using the #Mock annotation, Grails will mock the default methods (save/get/dynamic finders), but it doesn't do anything to any additional methods you may have added, so you need to manually mock those.

Resources