You've probably heard about Bobcat, our test automation framework? If not, check it https://cognifide.github.io/bobcat. Now, let's look under its hood. This is first in a series of posts that will discover some of our fierce cat secrets. How its internal mechanisms work? Why Guice works with custom annotations? How Page Objects are injected? How AEM authoring was solved? We start with Guice and listeners.
Introduction
There are few well-known facts about Bobcat:
- uses Java (1.8) and Selenium - Yes. Yet Another Selenium Framework
- uses Guice
- uses the Page Object concept
- allows testing AEM authoring
But how it is doing this? What mechanism lies under these facts? I won't explain everything, but maybe I will make it easier to understand or contribute to it. It's open-source after all.
Guice and Page Objects Injection
This is probably the most important part of the framework. Because dependency injection is used everywhere and Page
Objects are core of Bobcat. All this starts as most of Guice magic in module. In this case - PageObjectsModule.class
Guice module
bindListener(any(), new PageObjectTypeListener());
Multibinder<FieldProvider> fieldProviders =
Multibinder.newSetBinder(binder(), FieldProvider.class);
fieldProviders.addBinding().to(PageObjectProvider.class);
fieldProviders.addBinding().to(PageObjectListProxyProvider.class);
fieldProviders.addBinding().to(CurrentFrameProvider.class);
bind(Key.get(WebElement.class, CurrentScope.class)).toProvider(CurrentWebElementProvider.class);
bind(new TypeLiteral<List<WebElement>>() {
}).annotatedWith(CurrentScope.class).toProvider(CurrentScopeListProvider.class);
Only a few lines of code but a lot of things are going there. Some will be explained here, some we will cover in the future.
The basics:
- create listener of
PageObject
annotation - create different providers that will return injected objects in the correct scope
- create provider for
WebElement
within current scope (well actually both one object and list of them)
First Listener
PageObjectTypeListener
- thanks to him Page Objects can be used. This is TypeListener
which checks types that
Guice encounters. It is quite simple. If PageObject
annotation is present on the injected field then this listener
will call another one PageObjectInjectorListener
and let him handle the rest.
Main Listener
The listener above is the vanguard. It checks incoming objects and selects the correct ones.
PageObjectInjectorListener
is the main force of the Page Objects mechanism.
For each incoming objects it has four main tasks:
- select correct frame from the hierarchy of frames in the page
- inject all
WebElement
fields in object marked byFindBy
annotation - inject all Page Object fields - marked with correct annotations
- call object
PostConstruct
method
Of course the most interesting are two "inject" tasks, but this is a topic for part two.