JUnit 5: execute code once before multiple tests - kotlin

I'm working on a project which uses Quarkus to spin up a few REST endpoints. I have multiple integration tests which run with different test profiles or completely without a test profile. Heres an example:
#QuarkusTest
#Tag("integration")
#TestProfile(SomeProfile::class)
class IntegrationTestWithSomeProfile {
#Test
fun someTest() { ... }
}
#QuarkusTest
#Tag("integration")
class IntegrationTestWithoutProfile {
#Test
fun someTest() { ... }
}
Now I would like to execute a piece of code before the first test runs (or after the last test has finished). The problem is that #BeforeAll can only be used per class and I can't use Quarkus' start and stop events since Quarkus is started and shutdown multiple times - once for each different test profile.
Is there any hook (or hack - i don't mind dirty stuff as long as it works) which I could use, which would execute only once at the very beginning?

You can try #QuarkusTestResource with a class implementing QuarkusTestResourceLifecycleManager.
This can be used to start/stop services on the classes you want.
See: https://quarkus.io/guides/getting-started-testing#quarkus-test-resource

I finally found the solution I need. It's called TestExecutionListener. I went the route with adding a file called org.junit.platform.launcher.TestExecutionListener in resources/META-INF/services. Inside this file I've put the fqcn of my class implementing the TestExecutionListener interface.
In there I can then override testPlanExecutionStarted() and testPlanExecutionFinished(). With this, it doesn't matter how many TestProfiles we use and how many times Quarkus is started and stopped. The TestExecutionListener runs only once.

Related

How can I get junit tearDown method run if an exception is thown on startup?

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.

Autoloading in Laravel Tests

I'm trying to start writing tests within Laravel.
I think it is good practice to write my own basic TestCase, that runs the test setup (for example migrations).
<?php
class TestCase extends PHPUnit_Framework_TestCase {
public function setUp()
{
parent::setUp();
// just for example:
DB::table('categories')->first();
}
}
After that, I want to inherit all my TestCases from the one created above
<?php
class TestExample extends TestCase {
public function testSomethingIsTrue()
{
$this->assertTrue(true);
}
}
So now I have three problems:
My TestCase throws error, class DB not found. Why the heck is the test not autoloading Laravel Framework Classes?
PHPUnit tells me (with a warning) that my TestCase does not contain any tests, but that is my suspected behaviour for this class, how can I avoid this message?
My TestExample cannot find the class TestCase.
Can you help me understanding that?! How can I configure a test specific autoloading?
UPDATE:
Answer 1: Because I run the tests in NetBeans IDE, that needs to be configured! Setting up the right phpunit.xml helped
As revealed in our discussion, when you run your PHPUnit tests using your IDE (NetBeans, PhpStrom and so), you have to make sure your IDE is correctly configured to catch phpunit.xml.

JUnit - Postponed assert failure

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

Browser session in setUp(), tearDown(), no per testcase setup?

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.

How to get Selenium and TestNG to open one browser to run tests in multiple classes

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