6/16/2009

 

Multi bind in Guice 2.0

Guice is great tool to do dependency injection. But when you need to bind more than one implementation, or bind more than one instance, or bind a collection, things will be become tricky. After fighting with Guice for a long time, I think it is worth a while to document the tricks I've found.

Bind mutliple instances

Given we have two database to connect in my project. This code will not work
bind(SqlMapClient.class).toInstance(createSqlMapClientForDB1());
bind(SqlMapClient.class).toInstance(createSqlMapClientForDB2());
It will not work not only because Guice do not allow you bind same key(SqlMapClient.class is the key in this case) twice, but also when we use the dependency.
public class Service1 {
  @Inject
  SqlMapClient sqlMapClient;
}
How can we know which database connection we've got? This is a well-known problem, and has been addressed since the 1.0. In 1.0, we have three choices to make it work:

Choice 1: Different Type

We can use inheritance to make two SqlMapClient instances of different type.
public class Db1SqlMapClient extends SqlMapClient {
  private final SqlMapClient delegate;
  // delegate all methods of sql map client
}
public class Service1 {
  @Inject
  Db1SqlMapClient sqlMapClient;
}

Choice 2: Binding Annotation

Key in Guice is not necessary the type itself, it could be type and binding annotation. Use binding annotation, we can bind same type multiple times, although each binding is still using different key (different binding annotation).
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME
public @interface DB1 {
}

bind(SqlMapClient.class)
.annotatedWith(DB1.class)
.with(createSqlMapClientForDB1());

public class Service1 {
  @Inject @DB1
  SqlMapClient sqlMapClient;
}

Choice 3: Named Binding

Guice pre-defined a binding annotation called "Named". We can use "Names.named()" to create a instance of it.
bind(SqlMapClient.class)
.annotatedWith(Names.named("DB1"))
.with(createSqlMapClientForDB1());

public class Service1 {
  @Inject @Named("DB1")
  SqlMapClient sqlMapClient;
}
Although we have three choices, none of them is perfect. The first one is very tedious, and also the user of the SqlMapClient need to know the concrete type. The second one is better, but still the user need to know which one it depends on by annotate its dependency. Still kinda of violated the principle of "Inversion of Control". Also, we need to define one more class for binding annotation. The third choice do not need us to define new class, but it is not refactoring friendly, and can not be found by finding references. So the recommanded way to do that is strong typed binding annotation.

Choice 4: Guice 2.0 Child Injector

In Guice 2.0, we can use child injector to define different binding to the same type.
db1Injector = injector.createChildInjector(new AbstractModule() {
  public void configure() {
    bind(SqlMapClient.class).toInstance(createSqlMapClientForDB1());
  }
});
db2Injector = injector.createChildInjector(new AbstractModule() {
  public void configure() {
    bind(SqlMapClient.class).toInstance(createSqlMapClientForDB2());
  }
});
Different database connection need to be injected by different injector. To use this style, your system has to be partitioned to be managed by several different containers. It is not practical in real world.

Bind Set

It seems easy, isn't it?
bind(Set.class).toInstance(new HashSet());
But what if we need to bind two set, one for set of integer, another for set of string. How to do that?
bind(new TypeLiteral<Set<String>>(){}).toInstance(new HashSet<String>(){{
  add("Hello");
  add("World");
}});
Or we can use the Types utility class introduced in Guice 2.0.
bind(Types.setOf(String.class)).toInstance(new HashSet<String>(){{
  add("Hello");
  add("World");
}});
This also seems easy. But how about the element of the set is not just a simple String. What if we have a interface called OrderProcessor:
public interface OrderProcessor {
  void processOrder(Order order);
}
Then we can have different OrderProcessor to process the order differently (send email, save the order into database):
public class MailOrderProcessor implements OrderProcessor {
  @Inject
  EmailSender emailSender
  // send mail
}
public class DbOrderProcessor implements OrderProcessor {
  @Inject
  SqlMapClient sqlMapClient;
  // save order to database
}
Ok, now how to bind set of order processor? Can we do this?
bind(Types.of(Set.class)).toInstance(new HashSet<OrderProcessor>(){{
  add(new MailOrderProcessor());
  add(new DbOrderProcessor());
}});
No, you can't. Because both of them have its own dependency. Manually newed instance will not inject those dependencies. To make it work, we have four choices:

Choice 1: Manually call injectMembers

@Inject
Injector injector;
for (OrderProcessor orderProcessor : orderProcessors) {
  injector.injectMemebers(orderProcess);
}

Choice 2: Wrapping the set

public class OrderProcessors {
  private final Set processors = new HashSet();
  @Inject
  public void setDbOrderProcessor(DbOrderProcessor processor) {
    processors.add(processor);
  }
  @Inject
  public void setMailOrderProcessor(MailOrderProcessor processor) {
    processors.add(processor);
  }
}

Choice 3: using getProvider

bind(new TypeLiteral<Set<Provider<OrderProcessor>>>(){}).toInstance(new HashSet<Provider<OrderProcessor>>(){{
  add(getProvider(DbOrderProcessor.class);
  add(getProvider(MailOrderProcessor.class);
}});
Here, we used the feature of AbstractModule called getProvider. Although we can not call injector.getInstance() inside a module, but we can get the provider of the instance. This way, what we got is a set of the provider of the processor, instead of a set of order processor. This might not what you want.

Choice 3: getProvider + ProvidedOrderProcessor

public class ProvidedOrderProcessor implements OrderProcessor {
  private final Provider<OrderProcessor> provider;
  public ProvidedOrderProcessor(Provider<OrderProcessor> provider) {
    this.provider = provider;
  }
  public void processOrder(Order order) {
    provider.get().processOrder(order);
  }
}
Now, we can get a order processor instead of the provider of it.
bind(Types.setOf(OrderProcessor.class)).toInstance(new HashSet<OrderProcessor>(){{
  add(getLazyInstance(DbOrderProcessor.class));
  add(getLazyInstance(MailOrderProcessor.class));
}});
OrderProcessor getLazyInstance(Class<? extends OrderProcessor> clazz) {
  return new ProvidedOrderProcessor(getProvider(clazz));
}
Not as easy as we thought, right?

Bind one collection by multiple modules

What if we want to bind one instance of set using multiple module? There is a extension to Guice allow us to do that.
public class Module1 extends AbstractModule {
  public void configure() {
    Multibinder<OrderProcessor> multibinder
         = Multibinder.newSetBinder(binder(), OrderProcessor.class);
    multibinder.addBinding().to(MailOrderProcessor.class);
  }
}
public class Module2 extends AbstractModule {
  public void configure() {
    Multibinder<OrderProcessor> multibinder
         = Multibinder.newSetBinder(binder(), OrderProcessor.class);
    multibinder.addBinding().to(DbOrderProcessor.class);
  }
}
Seems perfect? By the way, multibindings extension also support Map.

Limitation

But how about list? There is no official support to bind a list by multiple modules. Also, how to bind a chain of responsibilities (A.K.A decorators)?
public class DecoratedOrderProcessor implements OrderProcessor {
  private final OrderProcessor decorated;
  public OrderProcessor(OrderProcessor decorated) {
    this.decorated = decorated;
  }
  public void processOrder(Order order) {
    try {
      decorated.processOrder(order);
    } finally {
      // do something;
    }
  }
}
When we have multiple decorators, which formed a chain of responsibilities, then the scenario becomes complex. If there is only one module, then we can use similar techniques like "ProvidedOrderProcessor" to bind it. But if there are more than one modules need to bind a element of the chain, then there is no official way to do it.

Use Guice to build extension point

Comparing Guice and Spring, one advantage I see is Guice promotes the modular design. By grouping functionality into modules, we can see plug and unplug some implementation based on the environment and requirement (for example, test and production). It is also possible in Spring, to be fair, but it is just easier and more often used in Guice world. Using Guice, we can define something as default, then allow other module to be plugged in and override it. Here is a list of techniques you can use to make this kind of effect:

Choice 1: @ImplementedBy, @ProvidedBy

@ImplementedBy(MailOrderProcessor.class)
public interface OrderProcessor {
}
Then, in case all modules did not specify the binding for OrderProcessor, then the default one (MailOrderProcessor in this case) will be used. If there is a binding bind(OrderProcessor.class).to(DbOrderProcessor.class), then that one will be used. This feature is really neat, mostly in the case when we need to change something in the unit test environment.
@ImplementedBy
public interface CurrentTimeProvider {
  DateTime getNow();
  public static class DefaultImpl implements CurrentTimeProvider {
    public DateTime getNow() {
      return new DateTime();
    }
  }
}
In the production environment, the CurrentTimeProvider will automatically use the default implementation. But in the test, we can bind(CurrentTimeProvider.class).toInstance(new FixedTimeProvider(2008,5,12)); then we can write the test eaiser by fixing the time.

Choice 2: @Inject(optional = true)

public class ProcessOrderService {
  @Inject(Optional = true)
  OrderProcessor processor = new DummyOrderProcessor();
}
When the provider side can not pick a default implementation, but the user side do know its default choice, then we can annotate the dependency as optional, and set a default value to it. When there is no binding to OrderProcessor, then the feature will be disabled by using DummyOrderProcessor. This behavior can be changed by plugging new module providing a implementatio of OrderProcessor.

Choice 3: Multibindings

The extension of Guice we've mentioned above allow us to bind a set or map by multiple modules. Using this extension, we can allow new module to plug in their new implementation to modify the system behavior. Very useful way to provide extension point.

Choice 4: Module Override

This is a new feature of Guice 2.0. Easy to use, and "powerful".
Module finalModule = Modules
.override(new DefaultModule())
.with(new CustomizationModule());
If CustomizationModule defines same key as DefaultModule, the one defined in DefaultModule will be overriden. It is useful in some case, but I don't think it is a good feature. Instead, if possible, we should split big module into smaller modules, and compose them depending on our needs, instead of override them from outside. But, Modules.override opened a way to allow multiple modules to bind same list, or even a decorator chain:
Key CUSTOMIZABLE_KEY = Key.get(OrderProcessor.class, new Before(MailOrderProcessor.class));
bind(Types.listOf(OrderProcessor.class)).toInstance(new ArrayList<OrderProcessor>(){{
  add(new ProvidedOrderProcessor(getProvider(CUSTOMIZABLE_KEY));
  add(getLazyInstance(MailOrderProcessor.class);
}});
bind(CUSTOMIZABLE_KEY).toInstance(new DummyOrderProcessor());
Before is a binding annotation. In another module, bind CUSTOMIZABLE_KEY again then we can override it:
bind(CUSTOMIZABLE_KEY).to(getLazyInstance(DbOrderProcessor.class));

5/09/2008

 

Anemic Domain Model

Martin wrote a blog a long time before: http://www.martinfowler.com/bliki/AnemicDomainModel.html. It was about domain model without rich behavior (anemic). Today, I am going to analyze why we have this problem, and try to give a elegant solution. Let's give a example first. This is a task management system. Two entities in the domain, Employee, Task. So we can write the relationship as following codes:
public class Employee {
    private Set<Task> tasks = new HashSet<Task>();
}

public class Task {
    private String name;
    private Employee owner;
    private Date startTime;
    private Date endTime;
}
It is a very typical parent/child relationship. Now, I want to add a behavior to my domain model. The behavior is: get all the processing task owned by a specified employee. If we ignore the existence of database, very naturally, this behavior belongs to Employee entity.
public class Employee {
    private Set<Task> tasks = new HashSet<Task>();
    public Set<Task> getProcessingTask() {
       ...
    }
}
But if we do care the database. This design is not acceptable. Where can I get all my tasks? Are you going to load all my tasks when building the employee object? If we only have five tasks, that is OK. But if we have 5000 tasks, that probably is not acceptable. So, before the age of hibernate, we wrote:
public class TaskDAO {
   public Set<Task> getProcessingTasks(Employee employee) {
      ...//sql
   }
}
hmmm, wait a moment... Is DAO part of domain model. Yeah... you can. Just rename it to TaskRepository, then it is part of your domain model. Really? I don't believe it. DAO is not part of your domain model. Instead, it stole the logic from domain. It is the reason why our domain model is anemic. Because the getProcessingTasks was part of Employee, but now belongs to a DAO. Can hibernate solve the problem?
@Entity
public class Employee {
    @OneToMany
    private Set<Task> tasks = new HashSet<Task>();
    public Set<Task> getProcessingTasks() {
       ...
    }
}
yes! Hibernate rocks! Have we succeed? No, not yet. Hibernate can make the tasks lazy-loaded. But you only have two options. Load, or not. If you are iterating tasks inside the impl of getProcessingTasks, you still end up as loading all the tasks from the database. To solve this problem, many people tried many different ways. The goal was "injecting something" into domain, then domain can execute query itself. The attempts including using hibernate interceptor, static code instrument, aspectj... Spring gave a answer to this:
@Entity
@Configurable
public class Employee {
    private TaskDao dao;
    public Set<Task> getProcessingTask() {
        return dao.getProcessingTask(this);
    }
    public void setTaskDao(TaskDao dao) {
        this.dao = dao;
    }
}
The @Configurable annotation was introduced to inject DAO into domain model. Now, the domain can do what it supposed to do. Really? domain model depending on DAO made lots of people unhappy. The argued, the cyclic dependencies between DAO layer and Domain layer. The argued, domain should not be "bound" with database or any container. I personally think, it is not that a big issue... I think RoR Active Record is bounding the domain model with database, people still love it. Anyway, I started again, and looking for a more elegant solution. Finally, I found, what if I wrote this:
public class Employee {
    private RichSet<Task> tasks = new DefaultRichSet<Task>();
    public RichSet<Task> getProcessingTasks() {
        return tasks.find("startTime").le(new Date()).find("endTime").isNull();
    }
...
}
RichSet is a Set with extra capabilities (query, sum...)
public interface RichSet<T> extends Set<T> {
    Finder<RichSet<T>> find(String expression);
    int sum(String expression);
}
DefaultRichSet is pure in memory implementation of those operations by iterating the set. So you can new a Employee in your unit test, and test the getProcessingTasks right way. No need to worry about database or dependency injection. Do you feel better? But, where is the database? Er... This is complicated, you know. The first thing I need to do is mapping the entity in Hibernate. Er... hibernate do not like it. Hibernate expect a Set, not RichSet. I think I need to write more things to make hibernate happy:
<hibernate-mapping default-access="field" package="net.sf.ferrum.example.domain">
    <class name="Employee">
        <tuplizer entity-mode="pojo" class="net.sf.ferrum.RichEntityTuplizer"/>
        <id name="id">
            <generator class="native"/>
        </id>
        <set name="tasks" cascade="all" inverse="true" lazy="true">
            <key/>
            <one-to-many class="Task" />
        </set>
    </class>
</hibernate-mapping>
What is tuplizer? It is used by hibernate to replace your set with hibernate enhanced set. So, I wrote my own tuplizer, and replace your set with my enhanced set.
public class RichEntityTuplizer extends PojoEntityTuplizer {
    public RichEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
        super(entityMetamodel, mappedEntity);
    }

    protected Setter buildPropertySetter(final Property mappedProperty, PersistentClass mappedEntity) {
        final Setter setter = super.buildPropertySetter(mappedProperty, mappedEntity);
        if (!(mappedProperty.getValue() instanceof org.hibernate.mapping.Set)) {
            return setter;
        }
        return new Setter() {
            public void set(Object target, Object value, SessionFactoryImplementor factory) throws HibernateException {
                Object wrappedValue = value;
                if (value instanceof Set) {
                    HibernateRepository repository = new HibernateRepository();
                    repository.setSessionFactory(factory);
                    wrappedValue = new HibernateRichSet((Set) value, repository, getCriteria(mappedProperty, target));
                }
                setter.set(target, wrappedValue, factory);
            }

            public String getMethodName() {
                return setter.getMethodName();
            }

            public Method getMethod() {
                return setter.getMethod();
            }
        };
    }
}
In short, the code means:
employee.tasks = new HibernateRichSet<Task>(...)
This version of RichSet is much smarter. It will translate your find statements from
tasks.find("startTime").le(new Date()).find("endTime").isNull(); 
--->
DetachedCriteria.forClass(..).add(...).add(...)
Now, in the domain, you can query against your collection without worrying about how the query will be done. Domain is still pure, no dependency on DAO. Domain is still all InMemory, no need to start up your container, your database to test domain logic.

1/16/2008

 

Pain Points of Using XAML or WPF

Pain Point 1: XAML always create the controls by its default constructor

This means, you need to have a default constructor for you control, and the constructor will always be used by XAML. So, you can not use constructor dependency injection to pass things like services, gateways to your control. Also, you will not have chance to pass data in constructor, although the data might be must-have for the specific type of control

Paint Point 2: can not control XAML to create or not to create some part of GUI

Sometimes, the GUI is not static. It could be dynamic because the GUI would be different for the data it is presenting, such as for a meeting in the past it should show a adding note button, for a meeting in the future it should not. And more often, the security control requires the GUI to be different according to the role.

Paint Point 3: XAML is using XML, which contains too many visual noise

compared to things like YAML, XML is definitely not very friendly to our eyes. The things worse than XML I can come up is the braces of Lisp. Also, XML makes it harder to edit manually

Paint Point 4: Layouting in Grid

Using grid layout currently requires you to specify the row and column for all the children of a grid. It is very error-prone when the grid becomes large. But grid is a must-have for any non-trivial GUI, and there is not replacement for it yet.

Paint Point 5: Things not checked in compiling time

There are lots of things not checked by the compiler in XAML. Things like binding, resource looking up for example. And it is harder to cross reference between xaml and code.

Paint Point 6: More files

one file for xaml one file for cs. It requires more steps to create a new user control and is confusing to new comers.

Paint Point 7: Separating concerns

the default way events get handled is in the partial class of the XAML. It is not a good way of separating concerns and not good oo design. the windows and user controls usually doing too much in rich client application. It is not the fault of XAML in general, but it is not promoting a good model either by its weird way of hooking up event in xaml.

Paint Point 8: Hard to test

It is hard to test in many ways. First, not easy to inject dependency means you can not mock those expensive things like network connection. Second, creating a real window is taking more than ten seconds. Third, many things are in a static singleton model like resource looking up and the single instance application object.

Paint Point 9: Control lazy created with uncertain lifecycle

controls of list item in a list view were lazy created. We can not get those controls easily, and we even can not be sure they are created or not. only those who are visible will be created.

5/11/2007

 

Why Do We Need Mock Framework?

Given we have a simple behavior to test: a form a text field on the form a button on the form click the button, should set the text of the text field to "Hello" And, here is the code implementing the behavior, MVP pattern is applied here:
public interface View {
  public void setText(String text);
  public void addActionListener(ActionListener actionListener);
}
public class Presenter {
  public Presenter(final View view) {
    view.addActionListener(new ActionListener() {
      public void actionPerformed() {
        view.setText("Hello");
      }
    });
  }
}
Before writing the test in java, let's first write in pseudo-code:
create mock view
create presenter by mock view
fire event on mock view
assert text is set
Then, let's implement it using latest jMock:
@Test
public void test_click_button_should_set_text_hello() {
  Mockery mockery = new Mockery();
  final View mockView = mockery.mock(View.class);
  final ActionListenerMatcher actionListenerMatcher = new ActionListenerMatcher();
  mockery.checking(new Expectations() {
    {
      one(mockView).addActionListener(with(actionListenerMatcher));
      one(mockView).setText("Hello");
    }
  });
  new Presenter(mockView);
  actionListenerMatcher.fireActionPerformed();
  mockery.assertIsSatisfied();
}
Here, we introduced a custom matcher, called ActionListenerMatcher. The reason why we need this, is because we need a way to fire the event. Without the matcher, we have no place to store the listener passed in. Here is the implementation of ActionListenerMatcher:
public class ActionListenerMatcher extends BaseMatcher {
  private ActionListener actionListener;
  public boolean matches(Object item) {
    actionListener = (ActionListener) item;
    return true;
  }
  public void fireActionPerformed() {
    actionListener.actionPerformed();
  }
  public void describeTo(Description description) {
  }
}
What is the conclusion? The intention of developer when writing the test is lost in the long and complex mocking code. How about other frameworks? I have tried EasyMock as well, which is even worse than jMock. Do we have a simpler way? Yes, we have. Check this out:
@Test
public void test_click_button_should_set_text_hello() {
  MockView mockView = new MockView();
  new Presenter(mockView);
  mockView.fireActionPerformed();
  Assert.assertEquals("Hello", mockView.getText());
}
Isn't this simple? MockView is just a simple implementation of View:
private class MockView implements View {
  private ActionListener actionListener;
  private String text;
  public void addActionListener(ActionListener actionListener) {
    this.actionListener = actionListener;
  }
  public void setText(String text) {
    this.text = text;
  }
  public String getText() {
    return text;
  }
  public void fireActionPerformed() {
    actionListener.actionPerformed();
  }
}
So, before you starting to use a mock framework. Think about it, do we really need them?

8/10/2006

 

Async Unit Tesing in ActionScript3

This is a cutting-edge topic, if you are not interested in programming in next generation flash application, please ignore me:) The most mature unit testing tool available in ActionScript3/Flex2 world is FlexUnit2, which is a oss project hosted at labs.adobe.com. It is a simulation of JUnit, from framework design to usage. Nothing changed, except Thread.sleep is missing in Flash world. If we can not wait for a few seconds, how to test async behavior? To solve this problem, FlexUnit introduce a method in class TestCase, called "addAsync". It takes minimal two parameters. You can use it like this:
  loader.addEventListener(Event.COMPLETE, addAsync(onIndexPageLoaded, 1000));
The value returned by addAsync is a function wrapping your event handler. To look up full documentation about this method, see here. After adding a "Async", FlexUnit will wait for few seconds to finish testing one test method. But here are some findings and tips for you:
  1. FlexUnit is using "Timer" to wait for finishing. It will not pause execution actually, but will check for the result later. i.e:
      loader = new URLLoader();
      loader.addEventListener(Event.COMPLETE, addAsync(onIndexPageLoaded, 1000));
      loader.load(new URLRequest("twspike-index.html"));
      doSomething();
    
    doSomething will be executed immediately after loader.load(...).
  2. You can not use "addAsync" in setUp. Because setUp and testXXX is two different test cases, so FlexUnit will wait for setUp to finish instead of waiting for your actual testing code to finish. Currently, the error reported is quite mistery.
  3. How to actually wait for something happened than start testing? Here is the home made HOW-TO:
    private function onIndexPageLoaded(event:Event):void {
      parser = new IndexPageParser(loader.data);
      checker.call(this);
    }
    private function check(checker:Function):void {
      this.checker = checker;
      loader = new URLLoader();
      loader.addEventListener(Event.COMPLETE, addAsync(onIndexPageLoaded, 1000));
      loader.load(new URLRequest("twspike-index.html"));
    }
    public function blog_data_should_not_be_null():void {
      check(function():void {
        assertNotNull(parser.blogData);
      });
    }
    public function blog_data_should_be_valid_xml():void {
      check(function():void {
        XML(parser.blogData);
      });
    }
    
To summary it up: Wrapping your testing code in a function, Passing it to a check method, saving the testing code and start to execute the async action. In the event handler, call the saved testing code. It is annoying, I know...

6/30/2006

 

Simple Design: "DSL" Reloaded

I have been silent about DSL for a while. Now, I am back:) After being thinking for several months, I realized most of time, people don't need domain specific language, they just need the code read more nicely. Then, I come up a idea such kind of requirement doesn't need to involve heavy implementation such as grammar, parser or compiler, it could be simple, and it should be simple! So, is Ruby or Smalltalk the right selection? I have to say, I don't think so. The reason why I am not very keen about the idea using Ruby as the environment to embed so called "DSL"( I still refer to the nicely looking code as DSL, sorry about that), is because the language is not invented to support hosting DSL. Method missing or closure or initial block are not intended to use this way:
publishing agreement dated '9/20/2005'
with_author 'Joe W. Author', social('555-493-3920')
for_title 'DSLs for Dummies'

report do
  calculate 'Royalties', as net_retail_sales.during(last_six_months) * 20.percent
end
We are using Ruby too tricky!!! We are not using it, we are hacking it. The side effect is understanding the inner mechanism behind nice code becoming harder and harder, which is leading us to a dangerous direction. That reminds me of similar experience of C++. After introducing template into C++, I think except STL and several other excellent framework addressing some critical issue (mostly performance), others are simply too smart to be useful. Tons of frameworks inside Boost are just trying to make the code looking nicer... My point is if the language did not support the way of writting code we want, don't hack it using powerful trick to hack it even if the father of the language encourage you to do so. (I don't know the attitude of Matsumoto, but I do know Bjarne speaks a lot about extending C++ using framework). If not hacking a flexible scripting language, what can we choose to implement the so called DSL? The one thing I am sure if we need to write complex grammar for a new DSL (actually a English-like language), we are going the wrong direction. Because human language is too complex to be handled by formal grammar specification. So, I like the philosophy behind embeded DSL(Ruby again...or Smalltalk). The DSL is still embeded in a GPL, but the GPL should support hosting DSL so the implementation doesn't need to be tricky. The initial idea came into my mind back to this Feb. But at that time, I thought what we need is a new lightweighted GPL, but it still need to be weak typed, mordern featured scripting language just like Python, Ruby. Part of the reason is I had another nice idea about how to implement a weak typed scripting language effciently in JVM, but I didn't have passion to carry it into reality. This lead me to a not-that-simple-design... Sadly or luckily... I have to say, after several months, I realized it should be more simple than a new scripting language. Then, what is the simplest desing? How about this: assertThat(characterSet, contains('a')); compared with assert_$1$_contains_$2$(characterSet, 'a'); then fommatted to: assert characterSet contains a what we need is a Eclipse plugin to write and read java file in a different view. further more we can that Eclipse displaying inner class like a closure. and $this$ could aslo be a part of the method name to support: list.add(item); list.add_$1$_to_$this$(item); add item to list

5/24/2006

 

Integrating Selenium With Build Script

[Information Below is about Selenium-Core only] When I was at TWU, michael used three days to put selenium tests into build script. I even don't know the cc.net is actually running the selenium tests finally. From that time, I know, this is not a easy problem. Today it is my first time trying to setup a cc running selenium tests. It costs nearly a whole day to fight with resin and jdk bugs. Fortunately, I won the game at last:) Here is some tips I want to share: Steps:
  1. Start server
  2. Run Tests
  3. Get Result
  4. Stop server
Key Problem is "How to get the result". Selenium will call a url "postResult" After all tests were finished. The official solution to catch the result is writing a servlet. But here are two problems need to be taken into consideration: 1. When to stop the server? 2. How to know tests passed? To solve these two problems, the servlet need to "provide" information about the testing progress and result. So one side, the servlet is a result recevier from selenium; on the other side, it is the selenium testing information provider for build script. Here is my code:
  
public class SeleniumResultServelet extends HttpServlet {
 
 private String result = null;
 
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  OutputStream outputStream = response.getOutputStream();
  if (result == null) {
   outputStream.write("pending".getBytes());
  } else {
   outputStream.write(result.getBytes());
  }
  outputStream.write("\r\n".getBytes());
 }

 public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  result = request.getParameter("result");
 }

}
ps: selenium use "POST" to access postResult URL, build script use "GET" to access postResult URL. Inside the building script, there is a loop:
while(true) {
    Thread.sleep(500);
    URL url = new URL(postResultURL);
    InputStream inputStream = url.openStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    String status = reader.readLine().trim();
    reader.close();
    if ("failed".equals(status)) {
     throw new RuntimeException("Selenium Test Failed");
    }
    if (!"pending".equals(status)) {
     break;
    }
   }
Above it is my premature way to integrate selenium... ps: the reason why I write this part of build script in Java is not only because I need to integrate selenium, but mainly because Resin can not be stopped by ant under windows (I tried windows service, but failed). So please don't blame me about that...

This page is powered by Blogger. Isn't yours?

订阅 帖子 [Atom]