Symbiont and the Context Factory

In the post on using page objects with Symbiont, the focus was on how Symbiont leverages the page object to allow test scripts to separate intent from implementation. This also allowed for test script logic to be concise in terms of its expression. Here I’m going to focus on how we can get even slightly more concise by adhering to a factory pattern.

In the last post on this subject, you ended up with a page.rb file like this:

You also had a script.rb file like this:

When using page or activity definitions, the context is critical because it’s how your automation script knows where to execute a portion of its logic. In fact, the definition is the context. So the key thing from the script above is that you are always getting a page context that you can operate on. Each line above that instantiates a new page object and stores it in page is establishing that context. This does come at the expense of adding a bit of boilerplate to the code.

Since Symbiont encourages you to use the page object pattern, it accommodates that by providing a factory behind the scenes that will make establishing the context of a page object a little easier and read a little nicer. To do this you have to include the Symbiont factory and then make calls to defined factory methods.

Here’s a revised script:

In particular, note that the above code is including the Symbiont factory in this script as a mix-in. With the Symbiont factory available, you can make calls to a series of on context methods. Breaking it down a bit, consider just this:

Here, the on_view is a context action that says “on viewing the page that is represented by the Symbiote page definition.” In this case, the context knows to use the browser driver instance, if you provided one, or the built in instance that Symbiont will create, if you did not provide one. This context allows you to act on element definitions directly or on methods directly. The result of this is an instance of Symbiote, which means you can call methods on it, such as login_as.

This does bring up a good point, worth calling out. Just as you can call the methods on your page definition, you could use the factory to call element definitions as well. So if you think back to the original logic from the first post, you could use a series of factory calls like this:

That perhaps reads a little messier but it does work. That, however, brings up another point worth considering: factories can take blocks. So the above could be written as:

Going back to the on_view approach, I should note that “on view” can be read as “on viewing”. This means that the page will be called up in the browser, which further means the page definition must have a url_is assertion. This action will also perform the verified? check if your page definition includes url_matches and title_is assertions. If those assertions are not there, the verified? check will not be run.

Notice the next two lines do not use on_view, but instead use on. Why the difference? In those case since a page has already been viewed, and thus a browser initialized, the test script just needs to state that the following actions are taking place “on” a specific page. They don’t need to view the page because the previous action is taking the script to the correct spot. For example, after logging in, the navigation becomes available. The on(Navigation) logic is using the navigate_to method to go to the weight calculator page. The on(Weight) logic thus is able to convert a weight of 200.

Make sure you understand how the factories create instances. This is what allows Symbiont to reuse existing definitions so that the context is not lost and so that you are not establishing a new browser for every single test.

Factory Instances

The context factory methods will establish new instances of page or activity definitions but will also cache existing definitions so that they can be reused. For example, consider this simple script:

Now consider the following statements:

The output you will get is something like this:


These calls lead to two objects that are the same, which you can tell by their hex identifier. A call to a context factory provides an active page instance. In between each call, the above code shows you that @page is the same instance. Also note that this @page instance variable could be used just like you used the page variable in the original version of the script I started with at the top of this post. I want to make sure this is clear, so look at this logic again:

Realize that the above is replicated by this:

The call to on_view makes the @page variable available but also hides it unless you specifically want to call it. The idea of a context factory is that you use it for all of your automated test logic.

In my sample script where you ended up with two identical instances of Symbiote, what that is showing you is that the definition in question is cached so that it can be used during a given test execution. That works out good but what if you want a new instance for some reason? You can specify that a new context should be created. Consider this slightly modified logic:

The output will be something like this:


Here you can see there are two different objects. And, as you would suspect, that is as a result of the call to on_new. What the above code shows is that the context can be different when a new instance is specifically requested.

Factories and Simplicity

The main point to get out of this is a very simplified test script, that removes a lot of boilerplate, and that allows you to concisely express the intent of your tests by making the context and action being taken very clear. Further, with the delegation of actions to the page objects, your tests become very compressed but without, I would argue, sacrificing clarity.


This article was written by Jeff Nyman

Anything I put here is an approximation of the truth. You're getting a particular view of myself ... and it's the view I'm choosing to present to you. If you've never met me before in person, please realize I'm not the same in person as I am in writing. That's because I can only put part of myself down into words. If you have met me before in person then I'd ask you to consider that the view you've formed that way and the view you come to by reading what I say here may, in fact, both be true. I'd advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.