I'm automating functional tests using JUnit. And I ran into a problem: if I follow the rule "one (significant) assert per test method" then I end up with a bunch of 6-line test methods per one test case (17 is the biggest number yet). If I put them all into one test method I have to comment out failing asserts or leave half of the test never launched.
I don't like the first way because it launches browser for too many times, and it appears that browser launch + login/logout are more "expensive" and time consuming than test run itself.
The second way is no better because it introduces a lot of manual work in any case of managing it.
So, my questions are:
1. What are the best practices for such cases?
2. Is there some way to postpone test failure till the end of test? I mean, less important assert, that doesn't stop test run but causes it to fail in the end nonetheless.
UPD: Yes, I'm using Selenium. And I have a parent class for every test class to unify their settings.
You can use #BeforeClass and #AfterClass to launch and shutdown the Browser once for each test class or you can create a Rule that launches your Browser and use it with #ClassRule.
ErrorCollector may be helpful.
#BeforeClass
public static void beforeClass(){
launchBrowser();
}
#Before
public void before() {
login();
}
#AfterClass
public static void beforeClass(){
killBrowser();
}
That could be the answer to your problem
Related
I'm having an issue trying to do as the title says. When i run this locally, it does launch 2 chrome instances, however it uses only one of the browsers for both tests, as opposed to using each browser for each test. Any ideas how how to set this up correctly?
public class BaseClass
{
public IWebDriver driver;
[SetUp]
public void BaseSetUp()
{
driver = new ChromeDriver();
driver.Manage().Window.Maximize();
}
[TearDown]
public void BaseTearDown()
{
driver.Quit();
}
}
[Parallelizable(ParallelScope.All)]
[TestFixture]
public class DerivedClass : BaseClass
{
[TestCase("https://www.python.org/", "Welcome to Python.org")]
[TestCase("https://www.w3.org/", "World Wide Web Consortium (W3C)")]
public void Test3(string url, string title)
{
driver.Navigate().GoToUrl(url);
Thread.Sleep(4500);
Assert.AreEqual(driver.Title, title);
}
}
You are indeed creating the driver twice, but you are storing both instances in the same member field, driver. The reason this happens is that NUnit uses the same instance of a test class for all the tests within that class. (A new feature will be available in a future release to use a separate instance, but that's no help to you right now.)
As the tests run in parallel, the first test to run performs its setup and stores the driver in that field. Then the second test starts and stores it's instance in the same field. It's not possible to predict exactly when - during the execution of the tests- that replacement will take place. However, it will most likely happen consistently as you re-run on the same machine.
In the case of the example, the solution is simple. If you want each test case to use a separate driver instance, create the driver within the test itself, perhaps by calling a method that centralizes all your initialization.
An important thing to remember is that the ParallelizableAttribute is used to tell NUnit that the test may be run in parallel. NUnit accepts that as a promise, but if you share state between tests they won't run correctly.
This is a sample code which will login into system
#BeforeClass
public void verifyLogin()
{
loginObject.enterUsername("admin");
loginObject.enterPassword("!212313132");
loginObject.clickOnsignIn();
Assert.assertTrue(homeObject.isLoggedIn());
}
It's more about opinion.
You are doing login in #BeforeClass , it must be validated which you are doing correctly by doing Assert.assertTrue(homeObject.isLoggedIn()); this.
Because if login fails you might not wanna continue your testing.
Please note that #BeforeTest is bigger unit in TestNG as compared to #BeforeClass.
Personally I write login method in #BeforeMethod followed by all #Test methods in a class.
#BeforeClass is a setupClass in which you might want to declare your prerequisite such as driver initialization and all.
So coming to your question , where we should assert and what is good practice.
Wherever validation is required we must assert. It is as simple as that.
Common design pattern for Selenium is the Give-When-Then design.
Looks like your described test contains the 'Given user X is logged in' therefore 'When..' and 'Then..'. So as the logged in user is a precondition in your test, it makes a lot of sense to assert it.
However, it's not mandatory. You can make 'homeObject.isLoggedIn()' method to throw LoginException in case of not finding a WebElement in the logged in page. In this way you would be capable to create a negative test (wrong password for example) which would use exactly the same code and would expect LoginException.
I'm running selenium tests through junit.
In my system the setUp method of our AbstractSeleniumTestCase class sets up the selenium web driver and firefox profile, and the tearDown method logs out of the system and closes selenium.
Some tests will override the setUp and tearDown methods to do custom test setUp and tearDown.
The problem I'm having is that if an error accrues in the startUp method of a test (Like an unexpected popup or an selenium exception) then the web browser is never closed and the test specific tearDown operations are never done.
You could use a try block in the setUp() method to run tearDown() after encountering an error, and move the "meat" of the test setup into another method:
public void setUp() throws Exception {
try {
mySetUp();
} catch (Exception e) {
tearDown();
throw e;
}
}
Then, in your subclasses, override mySetUp() instead of setUp().
You should implement a TestWatcher and override finished method that, according to docs:
Invoked when a test method finishes (whether passing or failing)
I have not used JUnit for some time now, but as much as I remember, rules were applied before #Before.
Also you can init the driver in overriding starting method and take any relevant action by overriding the failed method etc. By that way it is possible to get rid of repetitive stuff on #Before and #After. Check the docs for specifics.
Then you can annotate your test classes with either #Rule or #ClassRule, google to understand which better suits your needs. For your any possible specific needs, it is also possible to create a rule chain.
I've previously written some selenium tests using ruby/rspec, and found it quite powerful. Now, I'm using Selenium with PHPUnit, and there are a couple of things I'm missing, it might just be because of inexperience. In Ruby/RSpec, I'm used to being able to define a "global" setup, for each test case, where I, among other things, open up the browser window and log into my site.
I feel that PHPUnit is a bit lacking here, in that 1) you only have setUp() and tearDown(), which are run before and after each individual test, and that 2) it seems that the actual browser session is set up between setUp() and the test, and closed before tearDown().
This makes for a bit more clutter in the tests themselves, because you explicitly have to open the page at the beginning, and perform cleanups at the end. In every single test. It also seems like unnecessary overhead to close and reopen the browser for every single test, in stead of just going back to the landing page.
Are there any alternative ways of achieving what I'm looking for?
What I have done in the past is to make a protected method that returns an object for the session like so:
protected function initBrowserSession() {
if (!$this->browserSession) {
$this->setBrowser('*firefox');
$this->setBrowserUrl('http://www.example.com/');
//Initialize Session
$this->open('http://www.example.com/login.php');
// Do whatever other setup you need here
}
$this->browserSession = true;
}
public function testSomePage() {
$this->initBrowserSession();
//Perform your test here
}
You can't really use the setupBefore/AfterClass functions since they are static (and as such you won't have access to the instance).
Now, with that said, I would question your motivation for doing so. By having a test that re-uses a session between tests you're introducing the possibility of having side-effects between the tests. By re-opening a new session for each test you're isolating the effects down to just that of the test. Who cares about the performance (to a reasonable extent at least) of re-opening the browser? Doing so actually increases the validity of the test since it's isolated. Then again, there could be something to be said for testing a prolonged session. But if that was the case, I would make that a separate test case/class to the individual functionality test...
Although I agree with #ircmaxell that it might be best to reset the session between tests, I can see the case where tests would go from taking minutes to taking hours just to restart the browser.
Therefore, I did some digging, and found out that you can override the start() method in a base class. In my setup, I have the following:
<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
class SeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase
{
public function setUp() {
parent::setUp();
// Set browser, URL, etc.
$this->setBrowser('firefox');
$this->setBrowserUrl('http://www.example.com');
}
public function start() {
parent::start();
// Perform any setup steps that depend on
// the browser session being started, like logging in/out
}
}
This will automatically affect any classes that extend SeleniumTestCase, so you don't have to worry about setting up the environment in every single test.
I haven't tested, but it seems likely that there is a stop() method called before tearDown() as well.
Hope this helps.
I am using Selenium with TestNG to test a website. I have created tests using the Selenium IDE and exported them to TestNG with each test being a method in a class. Eg,
For login tests there is a Login class which has methods testLogin(), testLogin2() etc
For signup tests there is a Signup class has methods testSignup(), testSignup2(), etc
I am using Ant to run the tests which works fine except that each class will open up a browser and then run its methods, eg, if I have five classes, then five browsers will open simultaneously and then run the tests.
What I want is to get Ant/Selenium/TestNG to just open up one browser and then run all the tests (in the same browser) in all the classes that I have specified in testng.xml. Using the example above, I want one browser to open then run testLogin(), testLogin2(), testSignup(), testSignup2().
If this cannot be achieved, then I want to open a browser, run all tests in a class then close the browser then open another browser then run the set of test methods in the next class.
Any help appreciated. Thanks in advance.
Today I have found the answer that works for me. Give me a few minutes to gather all code samples :)
Init.java
//base class that will be called before all tests
#Test(groups = "init")
public class Init{
DefaultSelenium browser;
public void start(ITestContext itc){
browser = (DefaultSelenium) itc.getAttribute("browser");
browser.open("url");
browser.click("xpath");
}
}
TemplateForClasses.java
/* - all public methods will be tests
* - all tests will be called after group init
* - just before suite will start we will start 1 browser instance
*/
#Test(dependsOnGroup="init")
public class TemplateForClasses{
DefaultSelenium browser;
#BeforeSuite
public void startBrowser(ITestContext itc){
browser = new DefaultSelenium(host,port,browser_type,url);
itc.setAttribute("browser",browser);
browser.start();
}
#AfterSuite
public void stopBrowser(ITestContext itc){
browser = (DefaultSelenium) itc.getAttribute("browser");
browser.stop();
}
//any other: #Before, #After methods
}
FirstGroupOfTests.java
//all tests classes will inherit preferences set in TemplateForClasses
public class FirstGroupOfTests extends TemplateForClasses{
public void FirstTest(ITestContext itc){
browser = (DefaultSelenium) itc.getAttribute("browser");
//browser.click("start");
}
}
idea:
start browser just once have tests
that run before every other tests(isBrowserRunning)
refer to browser from single test
This code was tested but currently I took it from the top of my head so possibly I will edit it tomorrow to make it more exact.
Update:
This result is based on testng.org documentation + some questions asked by me on stackoverflow + some answers found on several forums/groups
I must add I'm running testng programatically and I'm generating xml on the fly (as it is done on documentation.org). I am using it all in one package, I added package to the xml, included only classes Init + the ones that inherit from TemplateForClasses. If you need that xml, let me know.
I did this with Spring's dependency injection. And the init code is in a factory. I needed a way to have a Selenium instance shared not only between tests but between helper classes. Very seldom is selenium.someMethod() called directly in the tests. It more like helper.goToSomePage() or preferencesPage.changePassword(....).
It could be considered a bad idea to have a Selenium instance shared between tests, but the few bugs it brought were not that hard to find. The tests are run sequentially and the Selenium object need not be thread-safe. The state of the object must be kept consistent though.
For info, Spring is a Java framework and Dependency injection is only a part of it. Other DI frameworks like Guice can of course be used instead.
I too was stuck in the same problem for quite some time. I'll explain it in the simplest terms possible. Consider the following example:
Class A (contains the code selenium.start();)
|
|(inherited classes)
|--------class B }
|--------class C } Have some #Test methods
|--------class D }
Now everytime we run these test methods it will execute the code in the parent class constructor selenium.start(); Thats when the multiple browsers will all open up on your screen.
Now one by one the test methods will get executed - suppose tests in class B are executed they will be happening in one window, for class C another and so on.
So basically, all you have to do is remove the start() code from the parent constructor and put it somewhere in the classes B, C and D.
As long as you keep working with one selenium object everything will happen in one browser window. When you put start(); that browser will open (if it wasnt open) and a new session is created. stop(); and the session is terminated.
The flow of control goes like this=>
Class A, Class B
Class A, Class C
Class A, Class D
So if you can figure out a way to keep using the same selenium object with only 1 start() and 1 stop() for the entire execution sequence shown above, your test execution will happen in only one browser window.
If you put start() code in class A and stop code in each of B,C and D then you will have 3 windows open and one by one they will close as execution progresses.
If you put start() and stop() code individually in B,C and D then you will see one browser opening, executing test cases, closing. Another will then open, execute test cases for C, close etc.
Hope this helps. :-)