Chuan Chuan Law

DevOps | Software Automation | Continuous Integration

Year: 2016

How To Setup Json Log in Logback

  1. In pom.xml, add the dependency

<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.6</version>
</dependency>

2. In logback.xml, add the LogstashEncoder appender

 <appender name=”JSON” class=”ch.qos.logback.core.rolling.RollingFileAppender”>

//This is the JSON formatter
<encoder class=”net.logstash.logback.encoder.LogstashEncoder” />

//This is where the JSON log will be produced
<file>${catalina.base}/logs/webapp.log.json</file>

//On a daily basis, the JSON logged will be rolled over
<rollingPolicy class=”ch.qos.logback.core.rolling.TimeBasedRollingPolicy”>
<fileNamePattern>${catalina.base}/logs/webapp.log.json.%d{yyyy-MM-dd}</fileNamePattern>

//We only store the rolled over log for a max of 7 days
<maxHistory>7</maxHistory>
</rollingPolicy>
</appender>

3. Add the appender to the Root Level

<root level=”DEBUG”>
<appender-ref ref=”JSON” />
</root>

Pact – How To Set Up Provider In Play Framework

  1. In build.sbt, import the plugin
import au.com.dius.pact.provider.sbt._

SbtProviderPlugin.config ++ Seq(
  providers := Seq(
    ProviderConfig(name = "Provider", port=49600, requestFilter = Some(request =>
      request.addHeader("Authorization", "Bearer NTY5YjQwNGQtYjFkMi00N2UyLThiODgtYzQzNzBkNzlkMDQ5")
    )).hasPactWith(ConsumerConfig(name = "TestConsumer", pactFile = file("spec/pacts/test-consumer.json")))
  )
)

2. In a Makefile, have the following task:

run-provider:
   make run-app & \
   sleep 60
   sbt pactVerify

Where run-app is another Makefile task to spin up the app:

./activator "project test-server" ~run

We spin up the project, and then & will put it running in the background. We wait for 60 seconds for the project to be up. Finally we run Pact Provider.

Pact – Asserting The Function To Test from App Level

In your consumer test class, instead of calling the “subject.methodToTest()”,

For example:

describe MethodToTest{

before do

        AppleServiceClient.base_uri ‘localhost:1234’

    end

  subject { AppleServiceClient.new }

  describe “get_an_apple” do

    before do

        apple_service.given(“an apple exists”).

        upon_receiving(“a request for an apple”).

        with(method: :get, path: ‘/apple’, query: ”).

        will_respond_with(

          status: 200,

          headers: {‘Content-Type’ => ‘application/json’},

          body: {name: ‘Red Apple’} )

    end

    it “returns a an apple” do

      expect(subject.get_apple).to eq(Apple.new(‘Red Apple’))

    end

}

 

We could change our app to point to the mock localhost.

For example, in Ruby on Rails, we could change config->environments->environment.rb for the url for AppleServiceclient to point to the mock service at http://localhost:1234. Or better way is to create a new environment config for the Pact test.

# AppleServiceAPI
config.appleServiceApi_url = “http://localhost:1234”

Therefore, the new code in the assertion section will look something like:

  it “returns an apple” do

      expect(GetApple.get_apple).to eq(Apple.new(‘Red Apple’))

    end

where GetApple is the function in the application that makes the actual call to the AppleServiceClient.

How To Setup Pact Broker

Setting up Dockerized Pact Broker

The easiest and fastest way to do this is via Dockerized Pact Broker.

By following Step 1 to Step 4 in this document, you will be able to have a Dockerized Pact Broker linking to a Dockerized PSQL in your localhost within minutes!

All you then need is to publish the JSON file generated by your consumer test via Curl command:

curl -v -XPUT -H “Content-Type: application/json” -d @<path to JSON file> <http://localhost/pacts/provider/<Provider Name>I/consumer/<Consumer Name>/version/<App Version No.>>

  • Provider Name and Consumer Name can be found in the beginning of a Pact file
  • App version number is found in your consumer project’s code base

Older Version of Mac

A few things to share with my own experience is that (if you’re using Mac)

Pact Broker will be running on http://localhost

  • If you’re using an earlier Mac, therefore will be on Docker Toolbox, then will need to do the following to view the Pact Broker app:

docker-machine ip default

The command will enable the ip to be returned where you can view Pact Broker on port 80

Using Physical Database

If you do not wish yo use Dockerized PSQL, create your own database and set up the tables and users as explained. To link Pact Broker Docker to the physical database, do:

docker run –name pactbroker -e PACT_BROKER_DATABASE_USERNAME='{{user}}’ -e PACT_BROKER_DATABASE_PASSWORD='{{password}}’ -e PACT_BROKER_DATABASE_HOST='{{host}}’ -e PACT_BROKER_DATABASE_NAME='{{name}}’ -d -p 80:80 dius/pact_broker

 

Hello from New York!

G’day!

I haven’t been blogging for a while. This is because I have been busy settling in the Big Apple!

It was a big move and I have been very blessed to have the opportunity to explore the other side of the world.

I look forward to continue sharing what I have learned here. 🙂

 

Solving Peer Not Authenticated Issue for Maven repository on AWS

Below the problem I got while migrating the Jenkins slave jobs over to AWS:

 

 

error2

The way I solve it is to get a copy of compiled dependencies on the AWS instance and build a AMI image out of it. So, the AWS instance will already have a copy of all needed dependencies, and will only check out the test code and run the test without have to worry about downloading and compiling its dependencies.

 

Getting Your Tests Into CI – Jenkins

Test automation is not complete without CI –  getting it running automatically on a build box. I will show how to get a Selenium test suite running on Jenkins which I have been using personally.

I will assume you have already got Jenkins installed:

1. Click on New Item. Give the item a name and select Freestyle project and you will get to the Configure page

newItem

 

2.  Give a Project name

newItem

 

3. Under Source Code Management enter the URL of your code repository. I checkout out the code from Git in the example below.

newItem

 

4. Under Build Triggers, enter the schedule when you like your build to be run, using cron job syntax. The example means the build will run every Monday to Friday on 8.15am.

newItem

 

5. Under Build Environment, check on “Delete work space before build starts” (optional but always safe to do so)

newItem

 

6. Under Build, select the mechanism that you will use to invoke your test. In my case, I use Gradle. (you will need to install this on Jenkins prior under Configure). In the example below, firstly I build the test, then I run the task “cucumber” which will kick off the test. Remember to specify the Root Build script which is the root directory.

newItem

 

7. Finally, in Post-build Actions, specify how you would the result of the build to communicated. In the example below, I use “Publish cucumber results as a report” plugin and Slack Notifications.

newItem

 

8. Click Save and your Jenkins build is ready!

Accessing Oracle Database From Test Suite

Good test suites need to be able to access direct the database. Below is how I do it with my test suite in Groovy in Gradle framework accessing a Oracle database:

(1) Connect the database in your code

class TestDB {

    static void main(String[] args) {

        def sql = Sql.newInstance('jdbc:oracle:thin:@test.oracle.com.au:1231/testDb', 'username', 'password', 'oracle.jdbc.driver.OracleDriver')

 

(2) The special case with Oracle database is that is no ready to used library that we can download from Maven. I solve the problem by:

  • Download a copy of Oracle driver
  • Commit as part of the source code. I put under the Resource folder

file structure

 

  • In build.gradle, add the external Jar file as part of dependencies during compilation:
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.3'
    compile 'junit:junit:4.12'
    compile files('src/main/resources/ojdbc6.jar')
}

 

How To Replace Selenium With Integration Test

The trend and best practice in test automation is to move away from writing lots of functional test in Selenium. So, we want to try to replace high level Selenium test with Integration and Unit tests as much as possible.

Pros of Integration test:

  • Easier to write in comparison to Unit test (no need mocking)
  • Able to utilize test data in test database
  • Able to eliminate the need of some Selenium test

Cons of Integration test:

  • Data reliability issue due to dependency on test data

Grails testing framework provide a lot of convenience to achieve the above. The scenario below shows I eliminate the need of Selenium test:

  1. In DataSource.groovy change test to look point at test database
test {
    dataSource {
        username = "adam"
        password = "eve"
        url = 'jdbc:oracle:thin:@test.oracle.com.au:1521/adamAndEve'
    }
}

2.  Create an Integration test repo via command. The command will create a repo called “TestRepo” under “test” directory in the project file structure.

$ grails create-integration-test TestRepo

3.  Example of integration that:

  • Finds a book using its unique Id
  • Checks that the book title is correct
@Integration
class BookSpec extends IntegrationSpec {

    void "a book is returned"() {

        //Finding the item ID in the test database that you have defined in DataSource
        given:"we find a book with id"
          def bookId=Item.get(15102876765285400414)

        //Call Book object by passing the unique Id
        when: "we call the find function"
          def book=Book.findByItem(bookId)

        //Assert the Book object returned
        then: "book will be returned"
          Assert.assertTrue(book.title.equals("The Happy Pig"))
    }

4. Example of integration test that checks for UI. The test below:

  • Finds the book using Id
  • Check that the book displays a link named Buy Now
@Integration

class BookTagLibTests extends GroovyPagesTestCase {

    void testShowBuyBook(){

        //Find the book using Id
        def bookId=Item.get(15110576705972615055)

        //Checks that the book displays Buy Now link which will activate a JavaScript function upon click
        applyTemplate('<item:bookBanner item="${item}"/>', [item:bookId] ).contains("<a id='book-buy-${bookId.id}' class='btn carf na' href='javascript:buyBook(\"${bookId.id}\")' data-original-title='' title=''>Buy Now</a>")
    }

Both examples above shows that we could replace certain Selenium scenarios with Integration test, including those that involves UI.

Extending PACT Framework – Global Data Setup & Data Cleanup

In every testing framework we do data setup and clean up. Below is how I do it with Gradle framework:

Global Data Setup

Pact framework provides very good random test data generation with PactBodyBuilder. However, in some cases where our API involves data creation, we will need to remember the data we created, so we can use it across multiple tests, and then deleting it afterwards.

We can do this by using System Property in Gradle. Under test section in build.gradle, include this:

test{
    systemProperty 'RandomNum', new Random(1000)
}

In the Pact Consumer test where you need to create the data, do this:

  • Define the random number data that you want to generate and remember
class InsertCFCertConsumerTests {

    def ran=System.getProperty("RandomNum")

    @Test
    void "Insert CF Certificate" (){
  • In the Pact body, include your random number
withBody{
    requestId ran
}
  •   Of course, include this random number in the 2nd part of the Consumer test (service run)
VerificationResult result = CF_service.run() {

   def client = new RESTClient('http://localhost:1237/') 
   def body = new JsonBuilder([requestId: ran]) 
   def cf_response = client.post(path: '/v1/carfacts/',requestContentType: 'application/json',headers:['Content-Type': 'application/json'],body:body.toPrettyString())
  • The same random number ran can be called across multiple tests

Cleanup

To clean up data from database, create a task in build.gradle.

Clean up via database

  • Connect to the database
task cleanUp<<{
    gradle.class.classLoader.addURL(new File('src/main/resources/ojdbc6.jar').toURI().toURL())
    def sql = Sql.newInstance('jdbc:oracle:thin:@testDatabase.oracle.csuat.com.au:1521/test.test.office', 'USERNAME', 'PASSWORD', 'oracle.jdbc.driver.OracleDriver')

}

ojdbc6.jar is the Oracle database driver downloaded from the Oracle site . External file can be included during compilation by adding the following line under dependencies:

dependencies {
     compile files('src/main/resources/ojdbc6.jar')
}
  • Write the SQL to delete the data you want
String sqlDelete = 'delete from Table where id='VN000000000000001''
  • Execute the query
sql.execute(sqlDelete)

 

Clean up via API call

Sometimes, we could call a DELETE via API to do data clean up.

  • Get Id via database and delete via API call
task cleanUp<<{

   String sqlGetId= 'select id from Table where id='VN000000000000001''
   def delElas=new RESTClient('http://test.domain.com.au:9200') 
   def cfId=0 sql.eachRow(sqlGetId) { 
     println it.id cfId=it.id delElas.delete(path:"/id_checker/${cfId}") 
   }
}

However,  please note that the above will cause build task to fail as the data that you want to delete will not exist. Workaround for this is to exclude this task while building via command gradle build -x <name of clean up task>.

© 2019 Chuan Chuan Law

Theme by Anders NorenUp ↑