Stephan Schwab's Personal Blog


Making my IOS world a better place

My little IOS world just became a slightly better place. I figured out a problem that was bugging me for a while and had stopped my attempts of moving forward with ALE News.

IOS applications have to be signed when you want to run them on a real device. That makes sense for applications that are released and are supposed to be run on other people's devices. However, at the moment I'm just starting out with all of that and I only want to run my application on the IOS simulator.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

Because I don't yet have a developer certificate from Apple due to some issues with my Apple ID after an international relocation, I was unable to simple get the right certificate. xcodebuild didn't work for me as it wanted to sign the code.

Just now I found the solution.

xcodebuild build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

The two arguments at the end stop Xcode from looking for a certificate and now I can run make-world.sh successfully:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ALE News ........................................... SUCCESS [  0.324 s]
[INFO] common ............................................. SUCCESS [  1.208 s]
[INFO] ALE News Article Service Webapp .................... SUCCESS [ 44.274 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 45.969 s
[INFO] Finished at: 2014-10-27T16:47:33+08:00
[INFO] Final Memory: 26M/624M
[INFO] ------------------------------------------------------------------------
[INFO] Making IOS Application
Build settings from command line:
    CODE_SIGN_IDENTITY = 
    CODE_SIGNING_REQUIRED = NO

=== BUILD TARGET ScoopArticles OF PROJECT ALE-News WITH THE DEFAULT CONFIGURATION (Release) ===

Check dependencies

=== BUILD TARGET ALE-News OF PROJECT ALE-News WITH THE DEFAULT CONFIGURATION (Release) ===

Check dependencies

Validate build/Release-iphoneos/ALE-News.app
    cd /Users/sns/Documents/dev/ale-news-atdd/source/ios/ALE-News
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/sns/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/sns/bin"
    export PRODUCT_TYPE=com.apple.product-type.application
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/Validation /Users/sns/Documents/dev/ale-news-atdd/source/ios/ALE-News/build/Release-iphoneos/ALE-News.app

** BUILD SUCCEEDED **

[INFO] ------------------------------------------------------------------------
[INFO] Web Services & Application ..... SUCCESS
[INFO] IOS Application ................ SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

That produces a tested RESTful web service, a web client and an IOS application with share extension in one go.

Next step is now to spin up a web server with the web service deployed to it and run the IOS application on the simulator so that I can interact with the IOS application from a test script and have the IOS application talk to the web service on the local machine.

Safety instructions in the cafeteria

This week I made a little discovery in the cafeteria of my Chinese client. We finished our lunch a bit late and while we were still talking cooks and other kitchen personnel gathered.

The cooks were standing in two rows evenly spaced to the left and the other kitchen personnel was standing in two rows, equally evenly spaced, to the right. The image I saw reminded me of a military formation. The cooks were wearing the traditional white cook uniform and the other kitchen personnel had a white and blue uniform on.

There was no chatter amongst the people. They were all quite. Then someone else, dressed in shirt and black pants, showed up and got in front of the group. This person started to say something in a loud voice and from my translator I learned the meaning:

"What do you do when you leave your station" was the question.

"I turn off the stove and all other devices" was the answer in a single voice from all the cooks.

Similar questions were asked and the respective group, cooks and other kitchen personnel, answered like a chorus.

Making the world

So far I've managed to create a simple skeleton of an IOS share extension using a template that ships with Xcode. I'm still learning how that all works and I'm pretty much a complete novice at the topic.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

One thing that I still want to get right from the start is my fully automated build. I'm a big fan of the idea that you run a single command and that creates the whole software product in one go. That process may take a few minutes or a few hours. I expect it to get started with a single command and produce fully tested working software at the end that potentially can be shipped to a customer.

So I wrote a simple shell script called make-world.sh that first runs the already existing Maven build to produce the RESTful Article Service and the associated web client and then continues to build the IOS share extension using the Xcode tool chain on the command line.

At the very end it prints a summary.

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ALE News ........................................... SUCCESS [  0.277 s]
[INFO] common ............................................. SUCCESS [  0.867 s]
[INFO] ALE News Article Service Webapp .................... SUCCESS [ 44.078 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 45.367 s
[INFO] Finished at: 2014-10-19T13:23:44+08:00
[INFO] Final Memory: 26M/736M
[INFO] ------------------------------------------------------------------------
[INFO] Making IOS Application
=== BUILD TARGET ScoopArticles OF PROJECT ALE-News WITH CONFIGURATION Debug ===

Check dependencies
Code Sign error: No code signing identities found: No valid signing identities (i.e. certificate and private key pair) matching the team ID “(null)” were found.
CodeSign error: code signing is required for product type 'App Extension' in SDK 'iOS 8.0'

** BUILD FAILED **


The following build commands failed:
    Check dependencies
(1 failure)
[INFO] ------------------------------------------------------------------------
[INFO] Web Services & Application ..... SUCCESS
[INFO] IOS Application ................ FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILED
[INFO] ------------------------------------------------------------------------

Although the build fails at the moment that doesn't matter as the point is to have it automated.

Scenarios that depend on each other are bad

For the web client I originally created this specification for the Interact with article metadata archive feature:

Feature: Interact with article metadata archive

  Scenario: Create new article metadata
    When I submit the following article metadata
      | Author         | Title                                            | location                                                                                                     |
      | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
    Then the article metadata has been stored in the archive

  Scenario: List articles
    When I open ALE News
    Then I see a list of articles

Unfortunately at the moment I've managed to create a situation where scenarios depend on each other.

The known state of the system

For each scenario the state of the system should be known. If it happens to use a database, then that known state of the system should be an empty database or a database with known content.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

In the article service I have not yet hooked up a database but the simple array list does act as one. So I need to find a way to make sure that it is empty or has known content when I run a scenario.

The simplest solution for that seems to be:

  Background:
    Given there are no articles in the archive

I can implement that step by calling the article service with the HTTP verb DELETE for every article that it contains. I get the list of articles by using GET on the main resource /article.

With that background step the scenario Create new article metadata is now right but the second one List articles will fail.

The solution for now is to modify the second scenario.

Feature: Interact with article metadata archive

  Background:
    Given there are no articles in the archive

  Scenario: Create new article metadata
    When I submit the following article metadata
      | Author         | Title                                            | location                                                                                                     |
      | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
    Then the article metadata has been stored in the archive

  Scenario: List articles
    When I submit the following article metadata
      | Author         | Title                                            | location                                                                                                     |
      | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
    And I open ALE News
    Then I see a list of articles

With that out of the way I have a clean build again:

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ALE News ........................................... SUCCESS [  0.326 s]
[INFO] common ............................................. SUCCESS [  1.481 s]
[INFO] ALE News Article Service Webapp .................... SUCCESS [ 43.168 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 45.121 s
[INFO] Finished at: 2014-10-14T13:38:28+08:00
[INFO] Final Memory: 85M/646M
[INFO] ------------------------------------------------------------------------

I should probably rename the article-service module in the Maven project, because it now contains the service as well as the web client code. But I'm holding off on that for now, because there might be a way to separate the two again. I really don't want to couple the service and the web client. That goes against the whole idea behind it. But it is not my highest priority at the moment.

A more interesting client

As I was saying earlier, I don't expect the web client to be the main means of adding articles to the archive. Personally I read a lot of web content on my iPhone and iPad. What I use depends on the situation but it is almost always that I read on a mobile device.

I've started to explore the new IOS share extension API and will have to learn a bit more about the current IOS 8 development environment and brush up on my Objective-C knowledge. I will report soon how that goes...

Improving the fake storage

So far I've been using a simple static variable as a fake storage to hold article metadata. That was enough to move forward, to explore the API for the service and how to use it from a client.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

Another thing that was also in the back of my head is versioning. Sometime in the future the API of the service will evolve and I might have to distinguish different versions of it.

For now I just wanted to improve the storage a bit and changed this to an ArrayList. That change is now breaking my scenario tests, because I get a NullPointerException when my code is trying to use the list to find article metadata.

Initially I was using

    // todo: fake
    private static Map<String, ArticleMetadata> fakeArticles = new HashMap<String, ArticleMetadata>();

and then got the NullPointerException when trying to access fakeArticles. I then changed this to

    // todo: fake
    private static Map<String, ArticleMetadata> fakeArticles ;

    public ArticleResource() {
        fakeArticles = new HashMap<String, ArticleMetadata>();
    }

and now it is clear what is causing the problem: re-instantiating the class.

So I need to create a slightly more elaborate storage facility. I'm still not ready to invest into using a real database, as that would require me to do all kinds of additional work unrelated to working on what ALE News should do.

When I was browsing the documentation for Jersey I saw that it can be used together with Java CDI (Context Dependency Injection). I could have an instance of a class holding the ArrayList and have that injected into the @Resource to serve RESTful service requests.

That seemed like a nice way to continue going without committing on too much just yet.

However, it appears that for using CDI one needs to also use an Application Server. I looked around and found Glassfish, JBoss and even the real big ones like Websphere. That's not what I want to use at the point. Supposedly Jetty can also be configured to manage beans and support CDI. But somehow all that is a bit too much dependency for my taste. I consider choosing an application server or any slightly bit more elaborate runtime environment as a dependency an early commitment and I don't want to do that at the moment. I want to keep my options open and focus on fleshing out the what of ALE News. I might change a lot of things very soon and any early commitment would just make that change harder.

Remembering an old friend

Then I remembered an old friend: Spring Framework. Several years ago that was what a Java programmer would usually use for Dependency Injection. So I looked it up and saw that Pivotal Labs is now maintaining it. I don't know the details and I don't intent to research it but I thought "Oh! A Ruby shop has taken over a popular Java framework". The current spring.io website looks very good and there I also found that all I want to do at the moment I can do with Spring. That includes RESTful web services. I might want to continue with Spring instead of Jersey.

The good part about Spring has always been that one really owns the code. The framework itself is open source, which means that one can have a copy of it and it is not very likely to disappear. The other good part is that nothing really requires an application server. Everything can just as well be run as a standalone Java executable. So running something in a server becomes an option not a requirement.

Switching to Spring

Although it is not much right now I have a RESTful service implemented using Jersey. Jersey can integrate with Spring and so I decided to some minimal invasive changes to my code by adding a dependency:

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-spring3</artifactId>
    <version>2.12</version>
</dependency>

I also had to add

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

And an applicationContext.xml file to /src/main/resources

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="net.caimito.ale_news"/>

</beans>

Then I changed ArticleResource by adding a new class ArticleStorage and have Spring inject it into ArticleResource:

@Path("article")
public class ArticleResource {

    @Autowired
    private ArticleStorage articleStorage ;

ArticleStorage is simple:

@Repository
public class ArticleStorage {

    private Map<String, ArticleMetadata> metaData = new HashMap<String, ArticleMetadata>() ;

    public Map<String, ArticleMetadata> getStorage() {
        return metaData;
    }
}

After changing the methods in ArticleResource I was able to run mvn install on the whole project. The existing acceptance tests for the CRUD and list scooped articles feature that I have for the Article Service did all work but the ones for the web client did break.

Trouble using separate WAR files

Further investigation showed that Spring does not get initialized when Jetty loads the second war file.

So I researched and thought about that for quite a while. I even did an experiment using the Maven plugin for Glassfish.

Merging the modules

In the end I decided to merge the article-service and web-client modules. I'm not happy with that but for the moment it should be ok to continue with more important things. I need to come back to this issue though.

How to verify things

ATDD stands for Acceptance Test-Driven Development. If you will, it is some sort of outside-in TDD, which is test-driven development on the inside of software. With TDD we want to describe the behavior of the inner building blocks of software. Those building blocks in an object-oriented language would be the classes. With ATDD we want to describe the behavior of the software as a whole. It is about describing, in terms of observable behavior, what the software will do in reaction to eg. external events.

Let's have a look at the following scenario:

Scenario: Create new article metadata
When I submit the following article metadata
  | Author         | Title                                            | location                                                                                                     |
  | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
Then I see a list of articles

Submitting article metadata is certainly an external event. I'm not describing how metadata is submitted - I only mention the fact that it is submitted. So that is good, as I'm not giving away details of the implementation or prescribing the implementation in any way.

A specification is not the same as a blueprint. With a specification we don't want to prescribe how an implementor has to solve the given problem.

The Then step, however, may go a bit too far.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

If I look at it very closely, then I see an imperative description of the user interface. I see a list of articles means that the software will show me a list. So that kind of describes that, first, there will be a form to fill out - the submission in the first step - and, second, a list will be printed on the screen.

Maybe that's a bit too much?

For the article service a similar scenario was this:

Scenario: Create new article metadata
    When I post the following article metadata to the article service with URI "/article"
        | Author         | Title                                            | location                                                                                                     |
        | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
    Then an article ID is returned
    And the article metadata has been stored in the archive

That was for a piece of software that does not have a human computer interface (HCI) but instead communicates machine to machine.

For the article service returning the article ID is important, because RESTful services are expected to return a reference to the newly created resource. In the case of HCI the article ID is certainly irrelevant, because humans don't care about an ID.

So maybe the scenario for submitting new article metadata through the web client should read like the following?

Scenario: Create new article metadata
When I submit the following article metadata
  | Author         | Title                                            | location                                                                                                     |
  | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
Then the article metadata has been stored in the archive

How can we find out from the outside that the article metadata has been stored? We can use the article service. It allows us to query for a specific article. For that we will need to know the ID. Or it can list all articles that are currently stored and then we will have to search for the one we are interested in.

Let's try the latter:

@Then("^the article metadata has been stored in the archive$")
public void the_article_metadata_has_been_stored_in_the_archive() throws Throwable {
    GenericType<List<ArticleMetadata>> articleListType = new GenericType<List<ArticleMetadata>>() {};

    List<ArticleMetadata> actualArticles = ClientBuilder.newClient()
            .target("http://localhost:8080/article-service").path("/article")
            .request(MediaType.APPLICATION_JSON).get(articleListType) ;

    assertThat(actualArticles.size(), is(not(0))) ;
}

That seems to work. I have removed the log messages from PhantomJS for clarity.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running net.caimito.ale_news.web_client.RunCukesIntegrationTest
Feature: Article metadata archive

  Scenario: Create new article metadata                      # net/caimito/ale_news/web_client/articles.feature:3
    When I submit the following article metadata             # StepDefinitions.i_submit_the_following_article_metadata(ArticleMetadata>)
    Then the article metadata has been stored in the archive # StepDefinitions.the_article_metadata_has_been_stored_in_the_archive()

  Scenario: List articles         # net/caimito/ale_news/web_client/articles.feature:9
    When I open ALE News          # StepDefinitions.i_open_ALE_News()
    Then I see a list of articles # StepDefinitions.i_see_a_list_of_articles()

2 Scenarios (2 passed)
4 Steps (4 passed)
0m5.466s

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.875 sec - in net.caimito.ale_news.web_client.RunCukesIntegrationTest

Results :

Tests run: 6, Failures: 0, Errors: 0, Skipped: 0

Conclusion

There are basically two options for verification:

  • look up behind the scenes (database, service call, etc.)
  • verify through user interface

At the moment I have chosen to look up things behind the scenes. That gets me around the issue of imperatively describing how the application behaves in terms of human computer interaction.

However, at a later point I certainly will have to describe a bit of the user interface behavior. But then it's another story. I will come back to this.

What should the ALE News web client do?

Now that I have decided on my toolset for the acceptance tests I am thinking about what the web client should really do. The purpose of ALE News is to share articles people find. Where do people find interesting articles related to Agile and Lean? There is probably a huge number of sources and ways to find articles. It definitely is not only browsing the World Wide Web.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

These days I find myself following a number of people on Twitter. Whenever they post a link that sounds interesting, I follow it. Usually I read Twitter posts on my iPhone or iPad - rarely am I doing that on my laptop. That means that in my case a web browser based submission method would probably not be used by me - at least not frequently.

So I will have to develop some sort of extension to be used within the Twitter client on the iPhone and iPad - at least at some point.

Right now I'm facing a another little challenge: without any data in the article metadata archive there isn't much to display. That is why, if properly coded, this scenario will always fail:

Scenario: List articles
    When I open ALE News
    Then I see a list of articles

That is why I want to create a simple submission method so that I can use the web client to populate the archive. So for the moment the specification for the web client will be this:

Feature: Article metadata archive

  Scenario: Create new article metadata
    When I submit the following article metadata
      | Author         | Title                                            | location                                                                                                     |
      | Stephan Schwab | From competition it is a big leap to cooperation | http://www.stephan-schwab.com/china/culture/management/thoughts/2014/08/30/collaboration-or-cooperation.html |
    Then I see a list of articles

  Scenario: List articles
    When I open ALE News
    Then I see a list of articles

First try to fulfill the specification

I added two routes to the web client. One for looking at existing article metadata and another one for adding it.

articleApp.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/article/add', {
        templateUrl: 'partials/article/add.html'
      }).
      when('/article', {
        templateUrl: 'partials/article/list.html',
        controller: 'articleListController'
      }).
      otherwise({
        redirectTo: '/article'
      });

  }]);

To handle display and input of article metadata I am using the following controller implementation:

var articleControllers = angular.module('articleControllers', []);

articleControllers.controller('articleListController', ['$scope', '$http',
    function ($scope, $http) {
        $http.get('http://localhost:8080/article-service/article').success(function(data) {
            $scope.articles = data;
        });
    }]);

articleControllers.controller('articleAddController', ['$scope', '$http',
    function ($scope, $http) {
        $scope.add = function(article) {
            $http.post('http://localhost:8080/article-service/article', $scope.article).success(function(data) {
                $scope.articleId = data;
            });
        } ;
    }]);

Because I'm new to AngularJS and needed to experiment I decided to fumble around with it a bit before trying to test it. It is better to focus on one thing than to try to get multiple things working at the same time.

With the interesting parts of the rudimentary web client working I switched back to the executable specification. I added finding form elements and filling out the form to the step definitions and modified the entry point to the application. For the moment it is /web-client/app but I want to remove the app at some point. I will get back to that once I'm understanding AngularJS a bit better.

@When("^I submit the following article metadata$")
public void i_submit_the_following_article_metadata(List<ArticleMetadata> articleMetadataList) throws Throwable {
    ArticleMetadata metadata = articleMetadataList.get(0) ;

    driver.get("http://localhost:8080/web-client/app/#/article/add");

    driver.findElement(By.id("author")).sendKeys(metadata.getAuthor());
    driver.findElement(By.id("title")).sendKeys(metadata.getTitle());
    driver.findElement(By.id("location")).sendKeys(metadata.getLocation());
    driver.findElement(By.id("save")).click();
}

@When("^I open ALE News$")
public void i_open_ALE_News() throws Throwable {
    driver.get("http://localhost:8080/web-client/app");
}

With all these modifications in place I then executed mvn clean install and in the log I got this:

  Scenario: Create new article metadata          # net/caimito/ale_news/web_client/articles.feature:3
    When I submit the following article metadata # StepDefinitions.i_submit_the_following_article_metadata(ArticleMetadata>)
    Then I see a list of articles                # StepDefinitions.i_see_a_list_of_articles()
      java.lang.AssertionError: 
      Expected: is not an empty collection
           but: was <[]>
        at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
        at org.junit.Assert.assertThat(Assert.java:865)
        at org.junit.Assert.assertThat(Assert.java:832)
        at net.caimito.ale_news.web_client.StepDefinitions.i_see_a_list_of_articles(StepDefinitions.java:45)
        at ✽.Then I see a list of articles(net/caimito/ale_news/web_client/articles.feature:7)

PhantomJS was taking a screenshot for the first failure:

Screenshot Create new article metadata

With the web client and the article service deployed to Jetty I can also run my Cucumber feature in Intellij IDEA interactively:

Cucumber

The second scenario (List articles) passes and the screenshot shows this:

Screenshot List articles

The good news here is that I now have my own method for inputting data without relying on an external tool like CocoaRestClient that I need to operate manually. That is why the second scenario passes now.

The problem with the first scenario is that am using the existing step Then I see a list of articles to verify that adding article metadata was successful. That step wants to find a list of article metadata on the web page. I could redirect there after adding article metadata but I don't want to do that at the moment.

There are a few other considerations first. See the next blog post about those.

Selecting tools for testing the web client

After learning a bit about AngularJS to create some simple web client I want to continue on the ATDD path and write a specification before diving deeper into coding the web client.

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

Originally I had planned to use page-object and Cucumber for that. page-object makes it really simple to describe a web page and there is very little code required to interact with the page or check for the existence of elements. page-object is a Ruby gem. So I tried to add JRuby to my Maven build. However, I discovered that it is more effort than what I want to do at the moment. I like to keep things stupid and simple (KISS).

So far I'm using Java and JavaScript as programming languages for the behavior of my application. There is HTML and CSS for the presentation. If I were to use Ruby tools, then I would use two languages for the production code and introduce a third language for some of the test code.

As I'm working on this alone, I can use any mix of languages and technologies that I am familiar with. In the case of a development team there might be limitations to what can be choosen. The team might have dedicated testers who don't know Java but happen to know scripting languages like Ruby or Python. In that case JRuby might have been a good choice and the effort of integrating it into the single command build - mvn install at the moment - is certainly justified.

Step definitions in Java and Selenium to automate the web browser

So I decided to use Java to implement the steps of my scenarios - the acceptance tests - and use Selenium WebDriver to automate the web browser. I added the following additional dependencies to the pom.xml of the web-client module and also added the configuration for Maven Surefire to run my Cucumber features as part of the integration-test phase.

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>2.43.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.github.detro.ghostdriver</groupId>
    <artifactId>phantomjsdriver</artifactId>
    <version>1.1.0</version>
    <scope>test</scope>
</dependency>

PhantomJS as headless web browser

PhantomJS is a WebKit web browser that doesn't open a window and doesn't want a human to interact with it. It has become popular amongst web developers and is perfectly suited to run headless in-browser tests.

I downloaded and installed PhantomJS and wrote a simple scenario and corresponding step definitions to try out the whole setup.

public class StepDefinitions {
    private DesiredCapabilities capabilities = DesiredCapabilities.phantomjs();
    private PhantomJSDriver driver = new PhantomJSDriver(capabilities);

    @When("^I open ALE News$")
    public void i_open_ALE_News() throws Throwable {
        driver.get("http://localhost:8080/web-client");
    }

    @Then("^I see a list of articles$")
    public void i_see_a_list_of_articles() throws Throwable {
        assertThat(driver.findElement(By.id("articleList")), is(notNullValue())) ;
    }
}

The full source code for everything up to this point can be found at GitHub under a tag .

Running the full build

With those changes in place I was then able to go into the root directory of my project and run mvn install from there.

17:08 sns ~/Documents/dev/ale-news-atdd/source  (master)$ mvn clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] ALE News
[INFO] ALE News Article Service Webapp
[INFO] web-client Maven Webapp
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building ALE News 0.0.2-SNAPSHOT
[INFO] ------------------------------------------------------------------------ 

... some parts omitted ...

[INFO] ------------------------------------------------------------------------
[INFO] Building web-client Maven Webapp 0.0.2-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ web-client ---
[INFO] Deleting /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ web-client ---
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ web-client ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ web-client ---
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ web-client ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ web-client ---
[INFO] 
[INFO] --- maven-war-plugin:2.2:war (default-war) @ web-client ---
[INFO] Packaging webapp
[INFO] Assembling webapp [web-client] in [/Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/web-client]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp]
[INFO] Webapp assembled in [141 msecs]
[INFO] Building war: /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/web-client.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] 
[INFO] >>> jetty-maven-plugin:9.2.3.v20140905:start (start-jetty) > validate @ web-client >>>
[INFO] 
[INFO] <<< jetty-maven-plugin:9.2.3.v20140905:start (start-jetty) < validate @ web-client <<<
[INFO] 
[INFO] --- jetty-maven-plugin:9.2.3.v20140905:start (start-jetty) @ web-client ---
[INFO] Configuring Jetty for project: web-client Maven Webapp
[INFO] webAppSourceDirectory not set. Trying src/main/webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/classes
[INFO] Context path = /web-client
[INFO] Tmp directory = /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/tmp
[INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = file:/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = /Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp
2014-09-25 17:09:15.024:INFO:oejs.Server:main: jetty-9.2.3.v20140905
2014-09-25 17:09:15.150:INFO:oejsh.ContextHandler:main: Started o.e.j.m.p.JettyWebAppContext@5e98032e{/web-client,file:/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp/,AVAILABLE}{file:/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp/}
2014-09-25 17:09:16.410:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@4fe8f2ae{/article-service,file:/Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service/,AVAILABLE}{/Users/sns/Documents/dev/ale-news-atdd/source/web-client/../article-service/target/article-service.war}
2014-09-25 17:09:16.412:WARN:oejsh.RequestLogHandler:main: !RequestLog
2014-09-25 17:09:16.413:INFO:oejs.ServerConnector:main: Started ServerConnector@62158991{HTTP/1.1}{0.0.0.0:8080}
2014-09-25 17:09:16.413:INFO:oejs.Server:main: Started @40646ms
[INFO] Started Jetty Server
[INFO] 
[INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ web-client ---
[INFO] Failsafe report directory: /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/failsafe-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running net.caimito.ale_news.web_client.RunCukesIntegrationTest
Feature: List articles
Sep 25, 2014 5:09:17 PM org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
INFO: executable: /Users/sns/bin/phantomjs
Sep 25, 2014 5:09:17 PM org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
INFO: port: 5855
Sep 25, 2014 5:09:17 PM org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
INFO: arguments: [--webdriver=5855, --webdriver-logfile=/Users/sns/Documents/dev/ale-news-atdd/source/web-client/phantomjsdriver.log]
Sep 25, 2014 5:09:17 PM org.openqa.selenium.phantomjs.PhantomJSDriverService <init>
INFO: environment: {}
PhantomJS is launching GhostDriver...
[INFO  - 2014-09-25T09:09:18.504Z] GhostDriver - Main - running on port 5855
[INFO  - 2014-09-25T09:09:18.650Z] Session [a1632c70-4493-11e4-9b90-892d0b7edf33] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true}
[INFO  - 2014-09-25T09:09:18.650Z] Session [a1632c70-4493-11e4-9b90-892d0b7edf33] - page.customHeaders:  - {}
[INFO  - 2014-09-25T09:09:18.651Z] Session [a1632c70-4493-11e4-9b90-892d0b7edf33] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"mac-unknown-32bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}}
[INFO  - 2014-09-25T09:09:18.651Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: a1632c70-4493-11e4-9b90-892d0b7edf33

  Scenario: List articles         # net/caimito/ale_news/web_client/articles.feature:3
    When I open ALE News          # StepDefinitions.i_open_ALE_News()
    Then I see a list of articles # StepDefinitions.i_see_a_list_of_articles()

1 Scenarios (1 passed)
2 Steps (2 passed)
0m2.208s

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.646 sec - in net.caimito.ale_news.web_client.RunCukesIntegrationTest

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- jetty-maven-plugin:9.2.3.v20140905:stop (stop-jetty) @ web-client ---
[INFO] Waiting 10 seconds for jetty to stop
2014-09-25 17:09:19.713:INFO:oejs.ServerConnector:ShutdownMonitor: Stopped ServerConnector@62158991{HTTP/1.1}{0.0.0.0:8080}
2014-09-25 17:09:19.721:INFO:oejsh.ContextHandler:ShutdownMonitor: Stopped o.e.j.w.WebAppContext@4fe8f2ae{/article-service,file:/Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service/,UNAVAILABLE}{/Users/sns/Documents/dev/ale-news-atdd/source/web-client/../article-service/target/article-service.war}
2014-09-25 17:09:20.224:INFO:oejsh.ContextHandler:ShutdownMonitor: Stopped o.e.j.m.p.JettyWebAppContext@5e98032e{/web-client,file:/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp/,UNAVAILABLE}{file:/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp/}
[INFO] Server reports itself as stopped
[INFO] 
[INFO] --- maven-failsafe-plugin:2.17:verify (default) @ web-client ---
[INFO] Failsafe report directory: /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/failsafe-reports
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ web-client ---
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/web-client.war to /Users/sns/.m2/repository/net/caimito/ale-news/web-client/0.0.2-SNAPSHOT/web-client-0.0.2-SNAPSHOT.war
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/web-client/pom.xml to /Users/sns/.m2/repository/net/caimito/ale-news/web-client/0.0.2-SNAPSHOT/web-client-0.0.2-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ALE News ........................................... SUCCESS [  0.388 s]
[INFO] ALE News Article Service Webapp .................... SUCCESS [ 36.820 s]
[INFO] web-client Maven Webapp ............................ SUCCESS [  6.003 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 43.371 s
[INFO] Finished at: 2014-09-25T17:09:20+08:00
[INFO] Final Memory: 45M/676M
[INFO] ------------------------------------------------------------------------

That is proof enough that I can still deliver at any time and it is now time to begin working on a specification that explains what the web client should do.

Learning AngularJS to create the ALE News web client

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

The ALE News web client will be implemented using the AngularJS web framework as a single page web application in JavaScript. To help me with the layout I'm going to use Twitter Bootstrap. I added NodeJS, NPM and Bower to my system and created a simple first HTML page:

<html>
<head>
    <title>ALE News</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="app/css/main.css">
</head>
<body ng-app ng-init="name = 'World'">
    <h1>Hello !</h1>

    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="bower_components/angular/angular.js"></script>
</body>
</html>

Running multiple web applications at the same time

As the web client will consume the article service, which comes in the form of another WAR file, I added the following Jetty configuration to my pom.xml. It will deploy both WAR files to separate context paths.

<build>
    <finalName>web-client</finalName>
    <plugins>
        <plugin>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <configuration>
                <webApp>
                    <contextPath>/web-client</contextPath>
                </webApp>
                <contextHandlers>
                    <contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
                        <war>${project.basedir}/../article-service/target/article-service.war</war>
                        <contextPath>/article-service</contextPath>
                    </contextHandler>
                </contextHandlers>
            </configuration>
        </plugin>
    </plugins>
</build>

With that in place I can do mvn jetty:run inside the web-client module and will get this output on the console:

2014-09-23 14:47:05.985:INFO:oejs.Server:jetty-8.1.16.v20140903
2014-09-23 14:47:06.157:INFO:oejpw.PlusConfiguration:No Transaction manager found - if your webapp requires one, please configure one.
2014-09-23 14:47:06.708:INFO:oejw.WebInfConfiguration:Extract jar:file:/Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service.war!/ to /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service
2014-09-23 14:47:08.076:WARN:oejsh.RequestLogHandler:!RequestLog
2014-09-23 14:47:08.090:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080

A quick test reveals that both applications can be accessed through their URL.

What is the simplest thing that could possibly work

I don't have any experience with the AngularJS web framework. With the help of their tutorial and some basic knowledge of JavaScript I was able to create a minimalistic application that consumes the article service and asks for a list of articles in the archive.

Here is the screen output from this simplistic webapp:

Minimalistic Ale News

This output is produced by the following HTML in index.html.

<html ng-app="articleApp">
<head>
    <title>ALE News</title>
    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="app/css/main.css">
</head>
<body ng-controller="ArticleListCtrl">
    <h1>ALE News</h1>

    <ul>
        <li ng-repeat="article in articles">
            <a href=""></a><br/>
            By 
        </li>
    </ul>

    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
    <script src="bower_components/angular/angular.js"></script>
    <script src="app/js/article-service.js"></script>
</body>
</html>

The ArticleListCtrl is defined in the article-service.js file:

var articleApp = angular.module('articleApp', []);

articleApp.controller('ArticleListCtrl', ['$scope', '$http',
  function ($scope, $http) {
    $http.get('http://localhost:8080/article-service/article').success(function(data) {
      $scope.articles = data;
    });
  }]);

Where did the data come from?

As the article service has only a fake data storage, a simple static instance variable, a GET request to obtain a list of articles will return an empty array. I used the CocoaRestClient to submit data to it via the POST method. That way I was able to use the service from the prototype web client.

So far I have experimented a little bit in order to learn more about how to use AngularJS. There was no point yet in writing a specification for the web client. That will follow next.

Adding the web client module

This blog post is part of a series about the development of ALE News using an approach to software development called Acceptance Test-Driven Development. For an overview please see the introductory article.

So far I've created a draft implementation of a RESTful service to interact with an archive of article metadata. Before fleshing out that service I want to add a web client to my project. The web client should consume the article service as its backend.

Need to deploy multiple web applications to the same servlet container

I learned that I will have to deploy the article service webapp and the web client webapp to the same servlet container. That seems to be doable with Tomcat but it appears to be easier with Jetty. I don't really care about the servlet container I'm using for this, so I decided to switch to Jetty.

Preparations

As I mentioned before, being able to deliver at any point in time is important to me. So before I actually start writing code for the web client I want to make all the necessary changes to the build configuration. That includes:

  • add a parent pom.xml
  • turn the article-service from a standalone Maven project into a Maven module
  • use dependency management in the parent POM
  • use Jetty as servlet container instead of Tomcat

Run the full single command build again

After all those changes I can run mvn install again and the output will now look like this (warnings removed for brevity):

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO] 
[INFO] ALE News
[INFO] ALE News Article Service Webapp
[INFO] web-client Maven Webapp
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building ALE News 0.0.2-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ ale-news ---
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/pom.xml to /Users/sns/.m2/repository/net/caimito/ale-news/ale-news/0.0.2-SNAPSHOT/ale-news-0.0.2-SNAPSHOT.pom
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building ALE News Article Service Webapp 0.0.2-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ article-service ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ article-service ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ article-service ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ article-service ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ article-service ---
[INFO] 
[INFO] --- maven-war-plugin:2.2:war (default-war) @ article-service ---
[INFO] Packaging webapp
[INFO] Assembling webapp [article-service] in [/Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/sns/Documents/dev/ale-news-atdd/source/article-service/src/main/webapp]
[INFO] Webapp assembled in [102 msecs]
[INFO] Building war: /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] 
[INFO] >>> jetty-maven-plugin:8.1.16.v20140903:start (start-jetty) > validate @ article-service >>>
[INFO] 
[INFO] <<< jetty-maven-plugin:8.1.16.v20140903:start (start-jetty) < validate @ article-service <<<
[INFO] 
[INFO] --- jetty-maven-plugin:8.1.16.v20140903:start (start-jetty) @ article-service ---
[INFO] Configuring Jetty for project: ALE News Article Service Webapp
[INFO] webAppSourceDirectory not set. Defaulting to /Users/sns/Documents/dev/ale-news-atdd/source/article-service/src/main/webapp
[INFO] Reload Mechanic: automatic
[INFO] Classes = /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/classes
[INFO] Context path = /article-service
[INFO] Tmp directory = /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/tmp
[INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = file:/Users/sns/Documents/dev/ale-news-atdd/source/article-service/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = /Users/sns/Documents/dev/ale-news-atdd/source/article-service/src/main/webapp
2014-09-23 14:31:31.651:INFO:oejs.Server:jetty-8.1.16.v20140903
2014-09-23 14:31:32.241:INFO:oejpw.PlusConfiguration:No Transaction manager found - if your webapp requires one, please configure one.
2014-09-23 14:32:01.870:WARN:oejsh.RequestLogHandler:!RequestLog
[INFO] Started Jetty Server
2014-09-23 14:32:01.887:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080
[INFO] 
[INFO] --- maven-failsafe-plugin:2.17:integration-test (default) @ article-service ---
[INFO] Failsafe report directory: /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/failsafe-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running net.caimito.ale_news.article_service.RunCukesIntegrationTest
Feature: CRUD and list scooped articles

  Scenario: Create new article metadata                                                   # net/caimito/ale_news/article_service/articles.feature:3
    When I post the following article metadata to the article service with URI "/article" # ArticleStepDefinitions.postArticle(String,ArticleMetadata>)
    Then an article ID is returned                                                        # ArticleStepDefinitions.an_article_ID_is_returned()
    And the article metadata has been stored in the archive                               # ArticleStepDefinitions.the_article_metadata_has_been_stored_in_the_archive()

  Scenario: Read article metadata                                                                     # net/caimito/ale_news/article_service/articles.feature:10
    Given some article metadata with key "550e8400-e29b-11d4-a716-446655440000" exists in the archive # ArticleStepDefinitions.some_article_metadata_with_key_exists_in_the_archive(String)
    When I ask the article service with URI "/article/550e8400-e29b-11d4-a716-446655440000"           # ArticleStepDefinitions.i_ask_the_article_service_with_URI_for_article_id(String)
    Then I receive the article metadata                                                               # ArticleStepDefinitions.i_receive_the_article_metadata()

  Scenario: Update article metadata                                                               # net/caimito/ale_news/article_service/articles.feature:15
    Given the article with key "550e8400-e29b-11d4-a716-446655440000" exists in the archive       # ArticleStepDefinitions.the_article_with_key_exists_in_the_archive(String,ArticleMetadata>)
    When I update details of the article with URI "/article/550e8400-e29b-11d4-a716-446655440000" # ArticleStepDefinitions.i_update_details_of_the_article_with_URI(String,ArticleMetadata>)
    Then the metadata for article "550e8400-e29b-11d4-a716-446655440000" in the archive is        # ArticleStepDefinitions.the_article_metadata_in_the_archive_is(String,ArticleMetadata>)

  Scenario: Delete article metadata                                                         # net/caimito/ale_news/article_service/articles.feature:26
    Given the article with key "550e8400-e29b-11d4-a716-446655440000" exists in the archive # ArticleStepDefinitions.the_article_with_key_exists_in_the_archive(String,ArticleMetadata>)
    When I delete the article with key "550e8400-e29b-11d4-a716-446655440000"               # ArticleStepDefinitions.i_delete_the_article_with_key(String)
    Then the article with key "550e8400-e29b-11d4-a716-446655440000" does not exist         # ArticleStepDefinitions.the_article_with_key_does_not_exist(String)

  Scenario: List article metadata                         # net/caimito/ale_news/article_service/articles.feature:33
    Given some articles exist in the archive              # ArticleStepDefinitions.some_articles_exist_in_the_archive()
    When I ask for a list of articles with URI "/article" # ArticleStepDefinitions.i_ask_for_a_list_of_articles_with_URI(String)
    Then I will receive a list of articles                # ArticleStepDefinitions.i_will_receive_a_list_of_articles()

5 Scenarios (5 passed)
15 Steps (15 passed)
0m2.060s

Tests run: 20, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.552 sec - in net.caimito.ale_news.article_service.RunCukesIntegrationTest

Results :

Tests run: 20, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- jetty-maven-plugin:8.1.16.v20140903:stop (stop-jetty) @ article-service ---
[INFO] 
[INFO] --- maven-failsafe-plugin:2.17:verify (default) @ article-service ---
[INFO] Failsafe report directory: /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/failsafe-reports
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ article-service ---
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/article-service/target/article-service.war to /Users/sns/.m2/repository/net/caimito/ale-news/article-service/0.0.2-SNAPSHOT/article-service-0.0.2-SNAPSHOT.war
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/article-service/pom.xml to /Users/sns/.m2/repository/net/caimito/ale-news/article-service/0.0.2-SNAPSHOT/article-service-0.0.2-SNAPSHOT.pom
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building web-client Maven Webapp 0.0.2-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ web-client ---
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ web-client ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ web-client ---
[INFO] skip non existing resourceDirectory /Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ web-client ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ web-client ---
[INFO] No tests to run.
[INFO] 
[INFO] --- maven-war-plugin:2.2:war (default-war) @ web-client ---
[INFO] Packaging webapp
[INFO] Assembling webapp [web-client] in [/Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/web-client]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/sns/Documents/dev/ale-news-atdd/source/web-client/src/main/webapp]
[INFO] Webapp assembled in [412 msecs]
2014-09-23 14:32:05.636:INFO:oejsl.ELContextCleaner:javax.el.BeanELResolver purged
2014-09-23 14:32:05.636:INFO:oejsh.ContextHandler:stopped o.m.j.p.JettyWebAppContext{/article-service,file:/Users/sns/Documents/dev/ale-news-atdd/source/article-service/src/main/webapp/},file:/Users/sns/Documents/dev/ale-news-atdd/source/article-service/src/main/webapp/
[INFO] Building war: /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/web-client.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ web-client ---
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/web-client/target/web-client.war to /Users/sns/.m2/repository/net/caimito/ale-news/web-client/0.0.2-SNAPSHOT/web-client-0.0.2-SNAPSHOT.war
[INFO] Installing /Users/sns/Documents/dev/ale-news-atdd/source/web-client/pom.xml to /Users/sns/.m2/repository/net/caimito/ale-news/web-client/0.0.2-SNAPSHOT/web-client-0.0.2-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ALE News ........................................... SUCCESS [  0.250 s]
[INFO] ALE News Article Service Webapp .................... SUCCESS [ 35.760 s]
[INFO] web-client Maven Webapp ............................ SUCCESS [  1.659 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 37.807 s
[INFO] Finished at: 2014-09-23T14:32:06+08:00
[INFO] Final Memory: 25M/614M
[INFO] ------------------------------------------------------------------------

By now I have the structure in place to get started with the web client. I can still ship the draft version of the article service. That allows me to continue with confidence.

Merge the changes into master

I always keep the master branch in the git repository clean and ready for release. The code in master should be working software - fully tested and ready for deployment.

With those major changes finished it is now time to merge by development branch into the master branch:

14:40 sns ~/Documents/dev/ale-news-atdd/source  (dev)$ git status
On branch dev
nothing to commit, working directory clean
14:40 sns ~/Documents/dev/ale-news-atdd/source  (dev)$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
14:40 sns ~/Documents/dev/ale-news-atdd/source  (master)$ git merge dev
Updating ed8dae2..d4c2ebb
Fast-forward
 .gitignore                                  |   5 +++-
 README.md                                   |   5 ++++
 article-service/pom.xml                     | 141 +++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------
 article-service/src/main/webapp/index.html  |   5 ----
 pom.xml                                     | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 web-client/.bowerrc                         |   3 +++
 web-client/bower.json                       |  26 +++++++++++++++++++
 web-client/pom.xml                          |  34 +++++++++++++++++++++++++
 web-client/src/main/webapp/WEB-INF/web.xml  |   7 +++++
 web-client/src/main/webapp/app/css/main.css |  36 ++++++++++++++++++++++++++
 web-client/src/main/webapp/index.html       |  23 +++++++++++++++++
 11 files changed, 306 insertions(+), 101 deletions(-)
 create mode 100644 README.md
 delete mode 100644 article-service/src/main/webapp/index.html
 create mode 100644 web-client/.bowerrc
 create mode 100644 web-client/bower.json
 create mode 100644 web-client/pom.xml
 create mode 100644 web-client/src/main/webapp/WEB-INF/web.xml
 create mode 100644 web-client/src/main/webapp/app/css/main.css
 create mode 100644 web-client/src/main/webapp/index.html
14:40 sns ~/Documents/dev/ale-news-atdd/source  (master)$ git push
Counting objects: 46, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (32/32), done.
Writing objects: 100% (38/38), 6.52 KiB | 0 bytes/s, done.
Total 38 (delta 9), reused 0 (delta 0)
To https://github.com/snscaimito/ale-news-atdd.git
   ed8dae2..d4c2ebb  master -> master
14:41 sns ~/Documents/dev/ale-news-atdd/source  (master)$ 

See a listing of all posts on this site