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.
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.
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.
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.
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:
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);
}
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.
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.
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