I wrote a set of feature files for testing a custom framework and I want to allow testing of specific implementations of the interfaces of the framework. I want to run a whole lot of features with different implementations.
To do that, I have created a custom ObjectFactory and passing implementations using PicoContainer dependency injection. I added this factory to a cucumber.properties file and it works just fine. The only problem is - what if I have more than one set of implementations to test?
I can create several ObjectFactories, but how can I run the tests multiple times with different factories? Is it possible to pass ObjectFactory implementation to Runner class, using annotation or something alike? I run features with JUnit runner, and if I can have several of them with different factories, it should work, I think. However the only option to specify ObjectFactory I've found is cucumber.options file which is one for a module...
Currently it is not possible to use multiple object factories in Cucumber. As a work around you could implement a single object factory that delegates to a different object factory depending on some environment variable.
You may also want to consider using cucumber-spring instead of cucumber-pico as cucumber-spring can pick up springs context configuration annotations from step definitions. This can be done with minimal configuration if you structure your project like this:
| - runners
| | - CucumberConfigATest.java // #CucumberOptions(glue="steps", extraGlue="config.a")
| | - CucumberConfigBTest.java // #CucumberOptions(glue="steps", extraGlue="config.b")
| - steps
| | - SomeSteps.java
| | - MoreSteps.java
| - config
| | - a
| | | - StepsWithContextConfigA.java
| | - b
| | | - StepsWithContextConfigB.java
#mpkorstanje provided an answer I came up with as well. In case someone needs an example of implementation - here it is:
#RunWith(Cucumber.class)
#CucumberOptions(features="src/test/resources")
public class MyRunner {
#BeforeClass
public static void setup(){
System.setProperty(EventProcessorPicoFactory.EVENT_BUS_HANDLER, IUserECNDataHandler.class.getName());
}
}
public class MyFactory {
public MyObject build() {
String type = System.getProperty("my.property.name");
switch (type) {
case "my.value":
return new MyObject();
default:
throw new IllegalArgumentException("not implemented");
}
}
}
Related
I'm migrating a Spring MVC project to Spring Webflux. For several reasons, we've decided to keep using spring-data-jpa, even if we have be careful about blocking and such. Our application supports multi-tenancy and the way we do that is by setting a special database variable for a connection before doing requests. The database takes care of the rest.
The class below is used in the spring.datasource.type parameter of the application configuration file.
class TenantAwareDataSource : HikariDataSource() {
override fun getConnection(): Connection = super.getConnection().also {
it.createStatement().use{
sql.execute("SET tnt_id = '" + getTenantId() + "'")
// ^ called with runBlocking because inline function is a suspend fun
}
}
fun suspend getTenantId(): String{
return Mono.subscriberContext()
.map { context: Context ->
context.get<String>("tnt_id")
}.log().flux().asFlow().toList()[0]
}
}
Our getTenantId() function was working based on the SecurityContext class, which uses ThreadLocal to keep the values. It was populated via a servlet filter using a JWT from the client. When shifting to Spring Webflux, we altered the filter to a WebFilter, and even managed to inject the needed value into the ReactiveSecurityContextHolder, like this:
class MyFilter: WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
return chain.filter(exchange)
.subscriberContext{it.put("tnt_id", getTentantFromJWT())}
}
}
In the Controller endpoint, we've managed to get the stored value:
#GetMapping("/rsch-test")
suspend fun reactiveSecurityContextHolderTest(): String? {
return ReactiveSecurityContextHolder.getContext().flux().asFlow().toList().toString()
}
However, this is not possible in our TenantAwareDataSource class, with the program logging:
| onSubscribe([Fuseable] FluxMapFuseable.MapFuseableSubscriber)
| request(64)
| onNext(empty)
| onComplete()
Whereas the same code will give off a different output if placed in the controller:
| onSubscribe([Fuseable] FluxMapFuseable.MapFuseableConditionalSubscriber)
| request(64)
| onNext(<tenant_id>)
| onComplete()
I'm guessing this happens because the reactive context is not propagated to this class. I realise using ThreadLocal is not an option because:
There's no guarantee that the Web Filter will run on the same thread as the SQL variable setting.
There's no guarantee that only one job will run on one thread at a time. There could be "collisions".
Do you have any ideas regarding this? Perhaps there's some workaround I've missed.
Thank you.
EDIT:
Added the code requested by #Toerktumlare regarding the attempt to retrieve the ReactiveSecurityContext.
I usually ignore blocks of code by applying the following attribute to classes or properties in C#:
[<DebuggerNonUserCode>] // Kludge for Portable Class Library support
For example, if I wanted to ignore type definitions when running code coverage, I would apply the attribute like this:
open System.Diagnostics
(* Types *)
[<DebuggerNonUserCode>]
type Form = {
Name:Name
Password:Password
}
[<DebuggerNonUserCode>]
type RegisterResponse =
| RegistrationNA
| FirstNameRequired
| LastNameRequired
| RegistrationSucceeded
The problem with this approach is that type definitions with this attribute aren't as readable.
Is there an alternative approach for ignoring blocks of code without having to use attributes when running code coverage?
I'm newbie to fitnesse and trying to run a simple calculator class which has "add" method accepting 2 parameters and returning the result. Can anyone help me to write the firnesse code for this, my method is as below
public int add(int a, int b) {
return a+b;
}
I believe you are trying to get a table like:
|AddFixtureTest |
|left|right|sum?|
|1 |1 |2 |
|2 |4 |6 |
This requires a java class like:
import fit.ColumnFixture;
public class AddFixtureTest extends ColumnFixture {
public int left;
public int right;
public int sum(){
return add(left, right);
}
private int add(int a, int b) {
return a+b;
}
}
See http://fitnesse.org/FitNesse.UserGuide.FixtureGallery.BasicFitFixtures.ColumnFixture
I assume Slim is fine by you and I'm gonna use script table for this (more out of habit):
|script| <class name> |
|add;| <variable1> | <variable2> |
As simple as that. And, make sure you are using the right libraries and pointing to the location of the where the class file is.
Example:
|import|
fitnesse.slim.test |
If you are interested in knowing why I have placed a semi-colon after "add", and how script table works, please go through this:
http://www.fitnesse.org/FitNesse.UserGuide.WritingAcceptanceTests.SliM.ScriptTable
You can get the FitNesse tutorial there for .Net code. I tried to describe how to use FitNesse for different types of testing.
If you want to check two parameters on the output you can do the following:
Return IEnumerable implementation with classes, which contains get
and set properties (see here). NetRunner will use get properties
if it can, otherwise it will use set properties. As a result, it will
set all available data first and then compare items left.
You can use out parameters in the tests, so you can return several different values and check them
The correct answer is:
|script:<classPackage>.<ClassName>|
|check|add;|8|8|16|
First tell the script to navigate to the correct class. In the next line you have to call either a method or a constructor. You can call the method by it's name (or fancy smansy digibetic words that vaguely match) and tack on a semicolon. But anything put into the pipe-blocks after that will be interpreted as parameters for that method. So how do you put the expected output?
The trick is to tell the FitNesse engine you need a result, with the keyword 'check'.
This will make the final pipe-block be the field for the expected result, in this case 16.
Here is a picture of the java code
Here is a picture of the text input and the FitNesse sceen result
Table example:
!script|SomeTest |
|Goto |$Url |
|check |IsAt|IndexPage|true|
|Index |CheckUserOrder? |
|0 |Name1 |
|1 |Name2 |
Code example:
public class SomeTest {
public string index;
public bool IsAt(string pageTitle){
//function for checking title of page
}
public string CheckUserOrder{
return username(index); // will get name of user for list which is other class
}
}
An exception is thrown: method name '0' not found in SomeTest...
I don't know why FitNesse is considering '0' as a method and not a parameter.
Are you working with the Slim test system? ColumnFixture requires the Fit test system. http://fitnesse.org/FitNesse.UserGuide.TestSystems
With Slim test system, use the DecisionTable http://fitnesse.org/FitNesse.UserGuide.SliM.DecisionTable
So your test will look like:
!|script|SomeTest|
|Goto|$Url|
|check|IsAt|IndexPage|true|
!|SomeTest|
|Index|CheckUserOrder?|
|0|Name1|
|1|Name2|
You are trying to combine a script and a decission table. If you are doing a script table, I expect you would have:
!|script|SomeTest|
|Goto|$Url|
|check|IsAt|IndexPage|true|
|check|CheckUserOrder|0|Name1|
|check|CheckUserOrder|1|Name2|
Is it possible to use multiple fitnesse fixture classes in one testtable (a scriptable i.e.) as in something like the following?
|script|FixtureClassOne,FixtureClassTwo|
|AMethodInFixtureClassOne|2|
|AMethodInFixtureClassTwo|2|
This is possible. You should load the fixture as a library. E.g.:
| import |
| my.fixtures.classpath |
| library |
| fixture 1 |
| another fixture |
| script |
| etc. |
No need to provide a fixture after the 'script' identifier anymore.
You do have to be aware that if the fixtures share non-unique method names you get into trouble
Objective: To have Multiple Fixtures associated with a single HTML Test Case
How: As you are working with fitnesses HTML Test case, you probably have at least a single fixture associated with it, which we will call default Fixture.
But in order to access another fixture (where control of execution passes to the other fixture), write a method in the default fixture class:
public Fixture run(String str) {
try {
Fixture fixture = Fixture.loadFixture(str);
return fixture;
} catch (Throwable e) {
// put your error handling here
e.printStackTrace();
}
return null;
}
From the Test case, pass the fully specified location with the project to the run() method. When the run() method returns the fixture to the HTML test case, it passes its execution via the new Fixture.