Closed jamesaslett1985 closed 3 years ago
let myButtonFunc browser = "#button"
is a function that takes in an argument called browser of type 'a
(generic) and returns a string.
You would need to change click myButtonFunc browser
to click (myButtonFunc browser) browser
for it to work but that is probably not what you are wanting.
Can you help me understand the purpose of myButtonFunc
? Maybe I can help you achieve your goal.
Thanks @lefthandedgoat, I'm in the process of knocking up a repo so you can fire it up - will probably make more sense that way. I'll be back once I've sorted it!
@jamesaslett1985 If you are wanting to create aliased functions so you dont have to pass in the browser every time you can use the other parallel option, the instanced one: https://lefthandedgoat.github.io/canopy//Api_Reference/canopy/canopy-parallell-instanced-instance.html
Its more c# style but should work also.
If thats not what you are wanting them I will look at your example once you get it posted.
Thanks @lefthandedgoat. Just to clarify, I'm creating a 'Page Object Model'-style framework, eg: I have a module per page of the website:
Module HomePage
let nextButton = "#nextBtn"
Because some of my let bindings are simply string selectors, I can perform canopy funcs on them at the test case level, which is quite nice, eg:
//Test case
click HomePage.nextButton
click SecondPage.button
In addition to this, I have funcs which will actually perform some canopy funcs, so I can call them nice and concisely like so:
Module HomePage
let forename applicant input = write $"#Applicant{applicant}_Forename" input
let lastname applicant input = write $"#Applicant{applicant}_Lastname" input
let name applicant forenameInput lastnameInput =
write (forename applicant) fornameInput
write (lastname applicant) lastnameInput
//Test case - just call the name func instead of both forename and surname
HomePage.name "1" "myFirstName" "myLastName"
I think I'm right in saying that with parallel.functions I can no longer use canopy funcs at the test case level, because I always have to pass in browser, and you can't pass that for just a string, eg: nextButton above. So I'd have to have something like:
Module HomePage
let nextButton browser = click "#nextBtn" browser
And call it like:
HomePage.nextButton browser
But with parallel.instanced, I have to use canopy funcs at the test case level, like so:
Module HomePage
let nextButton = "#nextBtn"
x.click (HomePage.nextButton)
I think I'm getting confused as to whether functions or instanced would be the best fit for my framework. Presumably there's no real difference in either approach other than stylistically? As you can probably tell, I'm new to F# :) Just trying to work out a 'best' approach before I go full steam ahead on the entire framework! I hope this all makes sense, because I am not entirely sure that it does!
I think you are understanding is pretty solid but let me rephrase it and that may help.
For canopy classic everything is static including the 1 instance of the browser etc. The way you have written helper functions is fine. The only change I would make is that for code like this:
let forename applicant input = write $"#Applicant{applicant}_Forename" input
let lastname applicant input = write $"#Applicant{applicant}_Lastname" input
let name applicant forenameInput lastnameInput =
write (forename applicant) fornameInput
write (lastname applicant) lastnameInput
I would change to something like
Module Register
let forename name = write name "foreNameSelector"
let lastname name = write name "lastNameSelector"
let fullName fname lname =
forename fname
lastname lname
// in test
Register.fullname "Chris" "Holt"
For parallel functions you are correct, you have to pass in the browser to all of your helpers so it would change to this
Module Register
let forename name browser = write name "foreNameSelector" browser
let lastname name browser = write name "lastNameSelector" browser
let fullName fname lname browser =
forename fname browser
lastname lname browser
// in test
Register.fullname "Chris" "Holt" browser
For the instanced version I would switch your page from a static class to an instanced based class also and you pass in the canopy instance into the class
Module Register
// add ctor
let forename name = x.write name "foreNameSelector"
let lastname name = x.write name "lastNameSelector"
let fullName fname lname =
forename fname
lastname lname
// in test
let x = new canopy
let register = new Register(x)
register.fullname "Chris" "Holt"
All that being said there is a different way to run in parallel. You can keep your code in the static canopy classic style which has the cleanest syntax, tag your tests for different workflows or parts of the system, and just run your nunit.exe multiple times and merge the results. Its been years since I had done this but using Jenkins, it had a plugin that would run jobs in parallel and merge the results into the 'meta' job and it would pass if all sub jobs passed. This let us run our tests in parallel across multiple machines without doing any heavy lifting in the actual tests.
Last note: You know how global variables are frowned on very heavily and why? When your tests are running in parallel, your database is a massive set of global variables and you have to take great care in how you write your tests and have them create new users etc so that your tests dont walk on each other and randomly fail when they misalign. Its can be very painful.
Hope this helps, best of luck on your journey!
Thanks man, that's all very useful info! I'll pour over it properly tonight. One last question if I may - does the fact that some funcs in Parallel. Functions
, eg: contains
don't take a browser
cause any issues, or will those work just fine without it?
It will work fine without browser because its more inline with a normal unit test assertion, and not specifically related to browser based testing:
Absolutely amazing, thank you @lefthandedgoat :)
Description
I am trying to get parallel execution working with my existing canopy framework. The examples in functionsTest.fs obviously work fine, however I'm having a hard time figuring out how to correctly pass the browser instance into each of my existing functions. I'm using NUnit but I don't think that's relevant to my issue:
Example
I also have another function which worked fine before, but now I get
Can't perform the action because the browser instance is null'.
Any help greatly appreciated, thanks :)