testng how to dynamically set groups from Factory? - selenium-webdriver

Before I setup a test class like the code below:
1. the Factory and test Dataprovider both used excel as the dataprovider.
2. In the Factory dataprovider table, it has a list of url
3. Each time, it will find one of the url in the factory dataprovider table, and run the test in each test methods..
public class Test {
WebDriver driver;
private String hostName;
private String url;
#Factory(dataProvider = "xxxx global variables", dataProviderClass = xxxx.class)
public GetVariables(String hostName, String url) {
this.hostName = hostName;
this.url = url;
}
#BeforeMethod
#Parameters("browser")
public void start(String browser) throws Exception {
driver = new FirefoxDriver();
driver.get(url);
Thread.sleep(1000);
}
#Test(priority = 10, dataProvider = "dataprovider Test A", dataProviderClass = xxx.class)
public void TestA(Variable1,
Variable2,Variable3) throws Exception {
some test here...
}
#Test(priority = 20, dataProvider = "dataprovider Test B", dataProviderClass = xxx.class)
public void TestB(Variable1,
Variable2,Variable3)
throws Exception {
some test here...
}
#AfterMethod
public void tearDown() {
driver.quit();
}
Now I want to dynamically assign different group for each test for different url. I am thinking add a variable 'flag' in the #Factory dataprovider:
#Factory(dataProvider = "xxxx global variables", dataProviderClass = xxxx.class)
public GetVariables(String hostName, String url, String flag) {
this.hostName = hostName;
this.url = url;
this.flag = flag;
}
That when flag.equals("A"), it will only run test cases in test groups={"A"}.
When flag.equals("B"), it will only run test cases in test groups ={"B"},
When flag.equals("A,B"), it will only run test cases in test groups ={"A","B"}
Is there any way I can do that?
Thank you!

TestNG groups provides "flexibility in how you partition your tests" but it isn't for conditional test sets. For that you simply use plain old Java.
You can use inheritance or composition (I recommend the latter, see Item 16: Favor composition over inheritance from Effective Java).
Either way the general idea is the same: use a Factory to create your test class instances dynamically creating the appropriate class type with the appropriate test annotations and/or methods that you want to run.
Examples:
Inheritance
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
public class DemoTest {
#Factory
public static Object[] createTests() {
return new Object[]{
new FlavorATest(),
new FlavorBTest(),
new FlavorABTest()
};
}
/**
* Base test class with code for both A-tests and B-tests.
*
* Note that none of these test methods are annotated as tests so that
* subclasses may pick which ones to annotate.
*/
public static abstract class BaseTest {
protected void testA() {
// test something specific to flavor A
}
protected void testB() {
// test something specific to flavor B
}
}
// extend base but only annotate A-tests
public static class FlavorATest extends BaseTest {
#Test
#Override
public void testA() {
super.testA();
}
}
// extend base but only annotate B-tests
public static class FlavorBTest extends BaseTest {
#Test
#Override
public void testB() {
super.testB();
}
}
// extend base and annotate both A-tests and B-tests
public static class FlavorABTest extends BaseTest {
#Test
#Override
public void testA() {
super.testA();
}
#Test
#Override
public void testB() {
super.testB();
}
}
}
Composition
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
public class DemoTest {
#Factory
public static Object[] createTests() {
return new Object[]{
new FlavorATest(),
new FlavorBTest(),
new FlavorABTest()
};
}
private static void testA() {
// test something specific to flavor A
}
private static void testB() {
// test something specific to flavor B
}
// only create A-test methods and delegate to shared code above
public static class FlavorATest {
#Test
public void testA() {
DemoTest.testA();
}
}
// only create B-test methods and delegate to shared code above
public static class FlavorBTest {
#Test
public void testB() {
DemoTest.testB();
}
}
// create A-test and B-test methods and delegate to shared code above
public static class FlavorABTest {
#Test
public void testA() {
DemoTest.testA();
}
#Test
public void testB() {
DemoTest.testB();
}
}
}
Your factory methods won't be as simple as you'll need to use your "flag" from your test data to switch off of and create instances of the appropriate test classes.

Related

Unit test Apache Camel specific routes by filtering (Model#setRouteFilter)

How to include only certain routes in my unit test. For example, how do I enable only my-translation-route.
public class TestRoute extends RouteBuilder {
#Override
public void configure() {
from("ftp://my-ftp-server:21/messages")
.routeId("my-inbound-route")
.to("direct:my-translation-route");
from("direct:my-translation-route")
.routeId("my-translation-route")
.bean(MyBean.class)
.to("direct:my-outbound-route");
from ("direct:my-outbound-route")
.routeId("my-translation-route")
.to("http://my-http-server:8080/messages");
}
}
I tried with Model#filterRoutes but this did not work. All routes were loaded.
class TestRouteTest extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() {
return new TestRoute();
}
#Override
public boolean isUseAdviceWith() {
return true;
}
#Test
void testIfItWorks() throws Exception {
context.setRouteFilterPattern("my-translation-route", null);
AdviceWith.adviceWith(context, "my-translation-route", a -> {
a.mockEndpointsAndSkip("direct:my-outbound-route");
});
context.start();
getMockEndpoint("mock:direct:my-outbound-route").expectedBodyReceived().expression(constant("Hahaha! 42"));
template.sendBodyAndHeaders("direct:my-translation-route", "42", null);
assertMockEndpointsSatisfied();
}
}
I got it working with the override of CamelTestSupport#getRouteFilterIncludePattern, e.g.:
#Override
public String getRouteFilterIncludePattern() {
return "direct:my-translation-route";
}
But then this is set for all tests in this test class.
Possible (stupid) solution : set a conditional auto startup for your routes, whose value depends on a (Camel or JVM) property that you can set with a particular value during the unit tests:
public class TestRoute extends RouteBuilder {
#PropertyInject(name="productionMode", defaultValue="true")
private boolean productionMode;
#Override
public void configure() {
from("ftp://my-ftp-server:21/messages")
...
.autoStartUp(productionMode); // <=here
}
}
There are various ways to override properties during your tests. See https://camel.apache.org/components/3.17.x/properties-component.html

Apache Camel Generic Router - pass exchange properties to static class methods

I am trying to create a generic router whose processor and other attributes are populated from a static class. Here is sample code.
public class GenericRouter extends RouteBuilder( {
#Override
public void configure() throws Exception {
from("direct:generic-route")
.process(Util.getProcesss(“${exchangeProperty[processKey]"))
.ToD(Util.getUrl(“${exchangeProperty[urlKey]"));
}
}
Public class Util{
Map<String,Object> routerResources;
static {
//load routerResources
}
public static Processor getProcessor(String processorKey){
return (Processor)routerResources.get(processorKey);
}
public static Processor getUrl(String urlKey){
return (String)routerResources.get(urlKey);
}
}
The generic router is expected to post a rest call. the properties "urlKey" and "processorUrl" are already available in exchange. I finding it difficult to pass exchange properties to static Util class methods.
If you want to access properties of an exchange in plain java you can use .process or .exchange. If you need to access body or headers you can use e.getMessage().getBody() and e.getMessage().getHeader()
from("direct:generic-route")
.process( e -> {
String processKey = e.getProperty("processKey", String.class);
Processor processor = Util.getProcessor(processKey);
processor.process(e);
})
.setProperty("targetURL").exchange( e -> {
String urlKey = e.getProperty("urlKey", String.class);
return Util.getUrl(urlKey);
})
.toD("${exchangeProperty.targetURL}");
Also make sure you fix the return type of this method:
public static Processor getUrl(String urlKey){
return (String)routerResources.get(urlKey);
}
As a side note, you can actually use map stored in body, header or property through simple language.
public class ExampleTest extends CamelTestSupport {
#Test
public void example(){
template.sendBodyAndHeader("direct:example", null, "urlKey", "urlA");
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
Map<String, String> urlMap = new HashMap<>();
urlMap.put("urlA", "direct:pointA");
urlMap.put("urlB", "direct:pointB");
from("direct:example")
.setProperty("urlMap").constant(urlMap)
.log("url: ${exchangeProperty.urlMap['${headers.urlKey}']}");
}
};
}
}

Properly Disposing a context with Unit of Work Pattern Dependency Injection in WPF

I have been trying to use DI within my Unit of Work / Repository pattern in WPF. The problem I am running into currently is if I make a call to a repository like _UserRepo.Add(User) and an exception is thrown. Every new call to the repository throws the exception because the context is never disposed of.
What I have tried
Unit Of Work
public class UnitOfWork : IUnitOfWork
{
private DbContextTransaction _trans;
private BomConfiguratorContext _context;
public UnitOfWork(BomConfiguratorContext context)
{
_context = context;
_trans = context.Database.BeginTransaction();
}
public void Dispose()
{
try
{
_context.SaveChanges();
_trans.Commit();
}
catch (Exception)
{
_trans.Rollback();
}
finally
{
_context.Dispose(); //This obviously does not work
}
}
}
Unit Of Work Factory
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private BomConfiguratorContext _context;
public UnitOfWorkFactory(BomConfiguratorContext context)
{
_context = context;
}
public UnitOfWork Create()
{
return new UnitOfWork(_context);
}
}
My Generic Repository
public interface IRepository<TEntity> where TEntity : class
{
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveRange(IEnumerable<TEntity> entities);
TEntity Get(int id);
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
void Update(TEntity entity);
}
Generic Repository Implementation
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly BomConfiguratorContext Context;
public Repository(BomConfiguratorContext context)
{
Context = context;
}
public virtual void Add(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
Context.Set<TEntity>().AddRange(entities);
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate);
}
public TEntity Get(int id)
{
return Context.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
public void Remove(TEntity entity)
{
Context.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
Context.Set<TEntity>().RemoveRange(entities);
}
public void Update(TEntity entity)
{
Context.Set<TEntity>().Attach(entity);
Context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
}
User Repository
public class UserRepository : Repository<User>,IUserRepository
{
public UserRepository(BomConfiguratorContext context)
:base(context)
{
}
}
Use Case
using (var UOW = _UnitOfWorkFactory.Create())
{
//Submit the user
_UserRepository.Add(ExampleNewUser);
}
So currently I am using MVVM Light to do all my DI work, now I understand with mvvm light you can only inject with singleton scope. So I am pretty sure I will end up having to switch over to something like Ninject so I can utilize their .InTransientScope or .InNamedScope (from what I have been reading).
Obviously the above code will not work with MVVM Light since the context is never properly disposed of.
The Question
So my question to you is if I were to swap over to using Ninject and start injecting my Context into these repositories / unit of work. How do I properly configure it to AWLAYS inject a new context within my unit of work for the repositories.
I read that Ninject MVC has .InRequestScope which would solve the issue entirely. But what about for WPF? How do you achieve the same kind of injection?
I can't seem to find the exact solution/pattern or maybe there is a better way to do this? Any suggestions and help would be greatly appreciated.
My solution to the problem was to create a ContextFactory.
Interface
public interface IContextFactory
{
BomConfiguratorContext Create();
BomConfiguratorContext Get();
}
Context Factory
The Factory allows me to either Get an existing context or create a new context.
public class ContextFactory : IContextFactory
{
private BomConfiguratorContext _context;
public ContextFactory(BomConfiguratorContext context)
{
_context = context;
}
public BomConfiguratorContext Create()
{
_context = new BomConfiguratorContext();
return _context;
}
public BomConfiguratorContext Get()
{
return _context;
}
}
New Base Repository
By calling the ContextFactory.Get() method I use the cached context instead of creating a new one.
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly IContextFactory ContextFactory;
public Repository(IContextFactory factory)
{
ContextFactory = factory;
}
public virtual void Add(TEntity entity)
{
ContextFactory.Get().Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
ContextFactory.Get().Set<TEntity>().AddRange(entities);
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return ContextFactory.Get().Set<TEntity>().Where(predicate);
}
public TEntity Get(int id)
{
return ContextFactory.Get().Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return ContextFactory.Get().Set<TEntity>().ToList();
}
public void Remove(TEntity entity)
{
ContextFactory.Get().Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
ContextFactory.Get().Set<TEntity>().RemoveRange(entities);
}
public void Update(TEntity entity)
{
ContextFactory.Get().Set<TEntity>().Attach(entity);
ContextFactory.Get().Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
}
New Unit Of Work Factory
When the factory is Create() method is called I call the context factory's Create() method to create a new context.
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private IContextFactory _contextFactory;
public UnitOfWorkFactory(IContextFactory factory)
{
_contextFactory = factory;
}
public UnitOfWork Create()
{
return new UnitOfWork(_contextFactory.Create());
}
}
By doing it this way I am now able to inject my context factory into all my repositories. I attempted to use the Ninject scopes mentioned above in the original question but ended up causing issues with injecting two separate contexts, one in my unit of work factory and one in my repositories.

MappingException: Ambiguous field mapping detected

Using Spring boot 1.5.6.RELEASE.
I have the following mongo document base class:
#Document(collection="validation_commercial")
public abstract class Tier {
#Id
private String id;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private Date created;
#Field("tran")
private Tran tran;
public Tier() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Tran getTran() {
return tran;
}
public void setTran(Tran tran) {
this.tran = tran;
}
}
which is then extended:
public class Tier1 extends Tier {
#Field("tier1")
private Tier1Programs tier1;
public Tier1() {
this.tier1 = new Tier1Programs();
}
public Tier1Programs getTier1() {
return tier1;
}
public void setTier1(Tier1Programs tier1) {
this.tier1 = tier1;
}
}
which in turn is extended:
public class Tier2 extends Tier1 {
#Field("tier2")
private Tier2Programs tier2;
public Tier2() {
this.tier2 = new Tier2Programs();
}
public Tier2Programs getTier2() {
return tier2;
}
public void setTier2(Tier2Programs tier2) {
this.tier2 = tier2;
}
}
There is a Tier1 Supervisor (Spring Boot Application) that uses the Tier1 class within the MongoRepository interface:
public interface Tier1Repository extends MongoRepository<Tier1,String>{}
for retrieving and saving - no issue.
I then have a Tier2 Supervisor (Spring Boot Application) that uses a Tier1 Repository (for retrieving the Tier1 document and a Tier2 Repository for saving the Tier2 document:
#Repository("tier1Repository")
public interface Tier1Repository extends MongoRepository<Tier1,String>{}
#Repository("tier2Repository")
public interface Tier2Repository extends MongoRepository<Tier2,String>{}
My service is:
#Service
public class TierService {
#Qualifier("tier1Repository")
#Autowired
private final Tier1Repository tier1Repository;
#Qualifier("tier2Repository")
#Autowired
private final Tier2Repository tier2Repository;
public TierService(#Qualifier("tier1Repository") Tier1Repository tier1Repository, #Qualifier("tier2Repository") Tier2Repository tier2Repository) {
this.tier1Repository = tier1Repository;
this.tier2Repository = tier2Repository;
}
public Tier1 findOne(String id) {
return tier1Repository.findOne(id);
}
public void SaveTier(Tier2 tier) {
tier2Repository.save(tier);
}
public Tier1Repository getTier1Repository() {
return tier1Repository;
}
public Tier2Repository getTier2Repository() {
return tier2Repository;
}
}
and finally the app:
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class})
#Configuration
#ComponentScan(basePackages = {"com.k12commercial.tier2supervisor"})
#ImportResource("classpath:application-context.xml")
public class Application implements CommandLineRunner {
#Autowired
private IReceiver raBidNetPriceReceiver;
#Autowired
private UdyDataSourceFactory udyDSRegistry;
public static void main(String[] args) throws InterruptedException {
try {
SpringApplication.run(Application.class, args);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run(String... args) throws Exception {
raBidNetPriceReceiver.processTierMessages();
exit(0);
}
}
When I run the Tier2 Supervisor from the command line I get the following error:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'tierService' defined in URL
[jar:file:/opt/java-commandline/tier2supervisor-1.0.jar!/BOOT-INF/classes!/com/k12commercial/tier2supervisor/service/TierService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tier2Repository': Invocation of init method failed; nested exception is org.springframework.data.mapping.model.MappingException: Ambiguous field mapping detected! Both private final java.lang.reflect.Type org.springframework.data.util.TypeDiscoverer.type and private final java.lang.Class org.springframework.data.util.ClassTypeInformation.type map to the same field name type! Disambiguate using #Field annotation!
I am not sure if the issue is Tier2 extending Tier1 (did try putting #Document tag above Tier1 and Tier2 with no change). I think I have marked the relevant fields so don't understand the need to disambiguate. I thought the issue was having 2 repositories (Spring Boot not knowing which one to DI) so removed the Tier1Repository - didn't work. Tried better qualifying the repositories but still got the same error. I made Tier1 and Tier2 #Transient and that got rid of the message but also removed the tier1 section in the mongo document - so wrong correction.
Thinking it is an annotation fix but not seeing it...
Please advise - thank you.
Sorry for the delay (I got pulled away to work on something else) and thank you to those who responded.
The issue was I had a MongoTemplate in my Tier level programs e.g.Tier2Programs (sub library) which Spring Boot was trying to autowire.
By moving the Mongo (CRUD) requirements to the supervisor level (I also replaced the Repositories with one MongoTemplate to simplify) I removed the ambiguity. (I also removed the Service class).
The code is contained with the RaBidNetReciever class
#Component
public class RaBidNetPriceReceiver extends BaseReceiver implements IReceiver, ApplicationEventPublisherAware {
private static final Logger LOGGER = LoggerFactory.getLogger(RaBidNetPriceReceiver.class);
private final RabbitTemplate raBidNetPriceRabbitTemplate;
public RaBidNetPriceReceiver(MongoTemplate mongoTemplate, RabbitTemplate raBidNetPriceRabbitTemplate) {
super(mongoTemplate);
this.raBidNetPriceRabbitTemplate = raBidNetPriceRabbitTemplate;
}
#Transactional
public void processTierMessages() {
try {
while (true) {
gson = getGsonBuilder().create();
byte[] body = (byte[]) raBidNetPriceRabbitTemplate.receiveAndConvert();
if (body == null) {
setFinished(true);
break;
}
tier1Message = gson.fromJson(new String(body), Tier1Message.class);
// document a 'Tier1' type so retrieve Tier1 first...
Tier1 tier1 = mongoTemplate.findById(tier1Message.getId(), Tier1.class);
Tier2Message tier2Message = new Tier2Message(tier1Message.getTran(), tier1Message.getId());
Tier2Process tierProcess = getTierProcess(tier2Message.getTran().getK12ArchitectureId());
Tier2 tier2 = new Tier2();
tier2.setId(tier1.getId());
tier2.setTier1Programs(tier1.getTier1Programs());
tier2.setCreated(tier1.getCreated());
tier2.setTran(tier1.getTran());
tierProcess.setTier(tier2);
tier2 = tier2.getTier2Programs().getRaBidNetPriceProgram().process(tierProcess);
mongoTemplate.save(tier2);
if (tier2.getTier2Programs().getRaBidNetPriceProgram().isFinished()) {
// publish event
publisher.publishEvent(new ProgramEvent(this, "FINISHED", tier2Message));
}
}
} catch (Exception e) {
LOGGER.error("id: " + tier1Message.getId() + " " + e.getMessage());
}
}
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
Thank you,

Java: how to "restart" a static class?

I have a static class (Foo) and a main class (Main)
See Main.java:
public class Main {
public static void main(String[] args) {
System.out.println(Foo.i); // 0
Foo.i++;
System.out.println(Foo.i); // 1
// restart Foo here
System.out.println(Foo.i); // 1 again...I need 0
}
}
See Foo.java:
public class Foo {
public static int i = 0;
}
Is there any way to restart or reset a static class?
Note: I need this because I'm testing a static class with jUnit and I need to clean parameters before second test.
EDIT
ALMOST SOLUTION:
Using StanMax answer, I can to this:
Main.java
public class Main {
public static void main(String[] args) throws Exception {
test();
test();
}
public static void test() throws Exception {
System.out.println("\ntest()");
MyClassLoader myClassLoader = new MyClassLoader();
Class<?> fooClass = myClassLoader.loadClass(Foo.class.getCanonicalName());
Object foo = fooClass.newInstance();
System.out.println("Checking classloader: " + foo.getClass().getClassLoader());
System.out.println("GC called!");
System.gc();
}
}
MyClassLoader.java
public class MyClassLoader {
private URLClassLoader urlClassLoader;
public MyClassLoader() {
try {
URL url = new File(System.getProperty("user.dir") + "/bin/").toURL();
URL[] urlArray = {url};
urlClassLoader = new URLClassLoader(urlArray, null);
} catch (Exception e) {
}
}
public Class<?> loadClass(String name) {
try {
return (Class<?>) urlClassLoader.loadClass(name);
} catch (Exception e) {
}
return null;
}
#Override
protected void finalize() throws Throwable {
System.out.println("MyClassLoader - End.");
}
}
Foo.java
public class Foo {
public static int i = 0;
static {
System.out.println("Foo - BEGIN ---------------------------------");
}
public void finalize() throws Throwable {
System.out.println("Foo - End.");
}
}
OUTPUT
test()
Foo - BEGIN ---------------------------------
Checking classloader: java.net.URLClassLoader#ec160c9
GC called!
MyClassLoader - End.
Foo - End.
test()
Foo - BEGIN ---------------------------------
Checking classloader: java.net.URLClassLoader#ec3fb9b
GC called!
MyClassLoader - End.
Foo - End.
PROBLEM: if I do the cast bellow:
Foo foo = (Foo) fooClass.newInstance();
I get error:
java.lang.ClassCastException
Only if you can unload class, get it re-loaded, as class static code gets executed when class is loaded.
But you can just directly modify the value:
Foo.i = 0;
(or create equivalent method for doing it, esp. if static member is not public)
Create a static method that sets the class variables to their initial values, then call it when you need it.
Avoid static.
It is well known that static is not testable and should thus be avoided. For example, avoiding static is one of the key motivations behind dependency injection. If you need one instance only at runtime, use the singleton pattern instead. And create a new instance for each test run.
You can try this.
Main MainObject = new Main;
MainObject.main(args);
It will restart the class again and again until you stop the class.

Resources