Transcripted Summary

Inevitably, we will need to find a way to configure our tests and framework.

There are many ways to do this, but I like to work with JSON or YAML files.



We'll start by making a simple Driver Factory class, then we'll make a JSON file to store our settings.


# Create DriverFactory

In Framework.Selenium, we're going to make a new file and call this “DriverFactory.cs”.

This is going to be a static class. We'll say public static class; just call it “DriverFactory”.

And this is only going to use one method, public static — we're going to be building an IWebDriver, and we're going to use the name of the browser to tell us which one we need.



Let's make sure we bring in the IWebDriver, so that's Selenium.

And now we'll use a switch statement on the browser name to tell us which one we need to build.

The first case is if the browser name is Chrome, we'll do a lowercase “chrome”, then we want to return a new Chrome driver and I'll just paste this in.

Then let's make sure we include our “Using” statement for Chrome. There we go.

For the second case, this one will be for Firefox. Now granted, we don't have the Firefox driver right now, but hey, we'll do it just for the example's sake.

And then the last piece is a default case, and that's if they pass in something that we are not supporting. So, if they pass in anything else that's not Chrome or Firefox, we're going to throw an ArgumentException.

We're going to say, hey friend, the browser name that you passed in is not supported. Please try again.


namespace Framework.Selenium
{
    public static class DriverFactory
    {
        public static IWebDriver Build(string browser)
        {
            switch (browser)
            {
                case "chrome":
                    return new ChromeDriver(FW.WORKSPACE_DIRECTORY + "_drivers"

                case "firefox":
                    return new FirefoxDriver();

                default:
                     throw new System.ArgumentException($"{browserName} not supported.");
            }
        }
    }
} 

And that's it for that.


Now we can go back to our Driver class.

Inside of here, first thing we'll do is pass in a browserName into our Init method.

And then we can replace this new ChromeDriver and just use our Driver Factory — DriverFactory.Build — and we'll build whichever browser name they pass in.


public static void Init(string browserName)
{
    _driver = DriverFactory.Build(browserName);
    Wait = new Wait(10);
}

Sweet.

And actually, this piece here [FW.Log.Info("Browser: Chrome”)] probably is more appropriate inside the DriverFactory, so let's cut and paste it into there.

We'll just put it right here.




namespace Framework.Selenium
{
    public static class DriverFactory
    {
        public static IWebDriver Build(string browserName)
        {
            FW.Log.Info($"Browser: {browserName}");

            switch (browserName)
            {
                case "chrome":
                    return new ChromeDriver(FW.WORKSPACE_DIRECTORY + "_drivers");

Then instead of this hard-coded Chrome, this is just the browserName that we're passing in and let's make sure we put in our dollar sign.

Sweet.


Okay, now our tests are going to be red because our Driver.Init now requires a browser name, so let's give it one.

In CardTests [SetUp], we say — Driver.Init(“chrome”) — and save.

Next one, CardDeckTests [SetUp], same thing:



There we go.


# Changes Without a Centralized Config

But that was gross. We had to change a few things in different places.

That goes against what we're trying to do with our framework.

What you want is the ability to make a change in a single location and have it affect many locations, you remember?


Having a config file, that is essentially your settings file, will not only centralize these options but make it very easy for you and others to make a simple change and effect all the test suites.


# Adding Configuration - The .json File

We will create a JSON file at the workspace root called framework-config.json and give it some simple values.


We'll start with the main object, so some curly braces, and the first object within this is just called “driver”.

In here we'll have a key value pair. The key will be “browser” and the value we'll have as “chrome”.


We'll then make a second object, this one we are going to call “test”.

And within this the key value pair and is going to be a URL, then let's do like a “staging.statsroyal.com” because I'm sure you probably have a QA or test environment.


{
    "driver": {
        "browser": "chrome"
    },

    "test": {
        "url": "staging.statsroyale.com"
    }
}

And that's it.

Now let's turn this JSON into code.


Inside of Framework, let's make a new class and we're going to call this “FWConfig.cs”.

Now we want to do here is to have these classes match what our JSON file looks like.


The first one is DriverSettings.

And inside of this we had a property who was a string called “Browser” and we give it the name of browser and the value of Chrome.


For the second one we had our TestSettings.

And this had a single property that was also a string called “Url”.


Last thing we need to do is just represent the main JSON object.

We're going to call this FwConfig.

And this had two objects:

  • If you remember it was the Driver object, which we're calling DriverSettings

  • Then it also had the TestSettings object. We'll just call this Test.


namespace Framework
{
    public class FwConfig
    {
        public DriverSettings Driver { get; set; }
        public TestSettings Test { get; set; }
    }

    public class DriverSettings
    {
        public string Browser { get; set; }
    }

    public class TestSettings
    {
        public string Url { get; set; }
    }
}

And that's it for our object.


# Set the Config in Our FW Class

Now we'll set this configuration in our framework class (FW.cs).

Since this config applies to the entire framework and test run, this would be considered a “singleton”. In other words, we really only need to set this once at the beginning of the test run.


Let's start with a private static field to hold our configuration.

We'll call it _configuration.


private static FwConfig _configuration;

And as you guys have seen many times before now, we'll make a public member for this that's going to hold this value.

We'll call this capital Config since this is going to be our public member.

  • This is going to be the_configuration, unless it's null.

  • If it is null, then we'll throw our NullReferenceException and we'll say, hey, “Config is null”, we need to SetConfig first.


public static FwConfig Config => _configuration ?? throw new NullReferenceException("Config is null. Call FW.SetConfig() first.");

Last thing we do in here is just to set that config.

So inside of here, let's make our static void SetConfig method.

And all we need to check for is:

  • If configuration is null, we'll give it a value
  • If it's not null, we don't need to worry about it

Let's grab the file, which is our "framework-config.json" and pull it into a string.

So, we're going to read all the texts. This is called "/framework-config.json".

And now we have the JSON as a String inside of that jsonStr variable.


Once we have that now we can set our _configuration.

We're going to convert the String into that FwConfig object — this is called deserializing.

We're going to Deserialize it into a FwConfig object and we're going to do that with that jsonStr.


public static void SetConfig()
{
    if (_configuration == null)
    {
        var jsonStr = File.ReadAllText(WORKSPACE_DIRECTORY + "/framework-config.json");
        _configuration = JsonConvert.DeserializeObject<FwConfig>(jsonStr);
    }
}

And there we go.

Finally, we can use it in code.


Back in Driver, we don't need this anymore.



And we can actually replace this browserName with FW.Config.Driver.Browser because we know that's Chrome.


public static void Init()
{
    _driver = DriverFactory.Build(FW.Config.Driver.Browser);
    Wait = new Wait(10);
}

# Use Our framework-config in Code

Moving to our tests now [starting with CopyDeckTests].

The first thing we need to do in our BeforeAll is to set the config [in the OneTimeSetUp].

That should be number one.


FW.SetConfig();

We'll then get rid of this [specifying “chrome”], since you don't need it anymore.

That's being handled by our factory.



And then we'll change this GoTo from this “statsroyale” to be the test Url — FW.Config.Test.Url.


[OneTimeSetUp]
public void BeforeAll()
{
    FW.SetConfig();
    FW.CreateTestResultsDirectory();
    FW.SetLogger();
    Driver.Init();
    Pages.Init();
    Driver.Goto(FW.Config.Test.Url);
} 

We’ll do the same thing in CardTests.

  • Don't need this anymore

  • And then replace this hard-coded string with FW.Config.Test.Url

  • Oh and of course, let's make sure that we do the FW.SetConfig() in here. Don't want to forget that

And now our tests are ready.


# Run CopyDeck Tests

Let's try running them.

I'm going to use the CopyDeck suite and let's see what we get back.


dotnet test --filter testcategory=copydeck

Ooh, too bad it looks like everything failed.


Well let's take a look into our log files to see what happened.

Scroll in here in TestResults.

Let's grab the first log and whoops, there it is.



Line 4 — “staging.statsroyale.com” — we actually can't hit that because it doesn't exist.

Let's make sure we change that in our “framework-config.json” to just go back to “statsroyale.com” because we know that exists.


{
    "driver": {
        "browser": "chrome"
    },

    "test": {
        "url": "statsroyale.com"
    }
}

Save it, and let's try rerunning the test again.

The exact same suite and let's see what we get back.

Huzzah, everything passed.

We're only holding 2 values in our JSON right now, but I've seen these config files get pretty big because of all the different settings that need to be stored or tracked.


Can you think of other things we could store in our JSON?

I sure can, but we're ready for the next chapter.



Challenge

I bet you already have some ideas for things we could put into our framework-config.json.

For this challenge, add a "waitSeconds" property to hold the default number of seconds that we want to use when instantiating our Wait class.

HINT:

  • We're instantiating Wait in Driver.Init()

  • Instead of using the magic, hard-coded number of 10, put that into our config JSON and then use the value in code



Resources



© 2021 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR