Tag Archives: ColdFusion

Managing ColdFusion code formatting


Hi all!

Since I use eclipse as ColdFusion editor, I have installed cfeclipse plug-in for ColdFusion.

I got two issues which makes me writing this blog post. One is slowing down editing of large file in cfeclipse and proper code formatting which improves readability.

Since cfelipse is now providing more features, eclipse is now taking more time and gives a slow response in typing. My project has some files with more that 1200 lines to 10k lines! So eclipse becomes time-consuming if we try to edit those files.

cfmodule and cfinclude are two options that can be used to reduce the file size by splitting main file by your functionality. But if you are still stick and you don’t want to use them, then you can use notepad++ for those files.

Most of the ColdFusion guys aware of Notepad++’s plugin for ColdFusion. It is simply great! But Notepadd++ and its plug-ins are not yet compared with eclipse with efclipse.

I reviewed my files and those files are also having leading spaces in code indentation. I think tab is the best way to have white spaces for code indentation, because if you can move cursor with your keyboard’s arrow keys faster than if we have spaces. It can also reduce number of characters of your file.

Recently cfeclipse’s update provides color formatting for variable scopes and color formatting within cfscript tag.
They are now also providing css, JavaScript and SQL code formatting as well as. But the main issue is we do not need newline for each ColdFusion tag. For example:

<input type="checkbox" name="mycheckbox" <cfif form.mycheckbox EQ 1>checked</cfif>/>

So auto code formatting with Ctrl+Shift+F will not give you nice and intelligent output.

So I decided to remove leading spaces with tab characters, without using auto code formatter.

To see the tab and space characters, just enable the “Show symbols” option.

notepad-optionWhat my logic is to find all leading spaces and replace 4 spaces with one tab character.

space-issueRun this code with providing your large file which has mix of spaces and tab in code indentation and see the output!

<cfif FileExists("#ExpandPath('/codeformatter_out.cfm')#")>
	<cffile action="delete" file="#ExpandPath('/codeformatter_out.cfm')#"/>
</cfif>
<cfloop file= "#ExpandPath('/codeformatter_source.cfm')#" index="theLine">
	<cfset ret = ReFind("^[ \t]+",theLine, 1, true) > <!--- RegEx to file leading spaces (tab+spaces) --->
	<cfif ret.len[1] GT 0>
		<!--- Find space count --->
		<cfset totalspace = len(rereplace(left(theLine,ret.len[1]),'[^ ]+',"","all"))>
		<!--- Find tab count --->
		<cfset totaltab = len(rereplace(left(theLine,ret.len[1]),'[^\t]+',"","all"))>
		<!--- 4 space should be converted to 1 tab --->
		<cfset totalnewtab = ceiling(totalspace / 4)>
		<!--- Calculate total tab to set --->
		<cfset finaltab = totaltab + totalnewtab>
		<!--- Create tab string to replace --->
		<cfset tabStr = "">
		<cfloop from="1" to="#finaltab#" index="i">
			<cfset tabStr = tabStr & "	">
		</cfloop>
		<!--- Replace leading spaces with our tab string --->
		<cfset theLine = ReReplace(theLine,"^[ \t]+",tabStr)>
		<!--- Remove trailing spaces --->
		<cfset theLine = ReReplace(theLine,"[ \t]+$",'')>
	</cfif>
     <cffile action="append" file="#ExpandPath('/codeformatter_out.cfm')#" output="#theLine#"/>
</cfloop>

Bug – count() function in ColdFusion query of query


Hi,

It has been a great time since I started working on ColdFusion. I must say that it is the easiest language in the world and yet it is a powerful and can be compared to any major programming language.

In last few days, I was using ColdBox and MockBox framework in our current project. I was using query of query which is very similar to SQL query, and we can use it over query object in ColdFusion.

Count() is a math function, but if we use it with select statement like count(*), it returns a number of rows fetched in select query. I used it query of query, and it was giving me the correct count if my select statement is returning some rows, but if it is not returning any row then ideally it should give 0 as output, but it gives [empty string].

I must need to come out of this issue, and I used a recordCount property of returning object, but you can also use CodlFusion function val() to convert the empty string to 0.

I can state that it is a bug base on the answer I get from the community. See my question on Stack Overflow Community. Still I will wait for some time if it is appropriate I’ll report it to ColdFusion bug tracking system.

ListView like Android Market Application with separator and ColdFusion as web server


Hi folks. This time I’m presenting a huge post.

This post will cover following things:

  • ListView with separator
  • Pagination  like Android Market application

I know that not all of you knows ColdFusion and its component (cfc), but it is just a server part like your web service code.

myWebservice.cfc

<cfcomponent displayname="myWebservice">
	<cffunction name="getJobs" access="remote" output="false" returntype="struct">
		<cfargument name="currentPage" required="true" type="numeric" default='1' />
		<cfargument name="totalRecord" required="true" type="numeric" default="10" />
		<cfargument name="searchString" required="false" type="string" default="" />
		<cfset var qResult = "">
		<cfset var condition = "" />
		<cfset var startRow = (arguments.CurrentPage-1) * arguments.TotalRecord +1 />
		<cfset var endRow = startRow + arguments.TotalRecord -1 />
		<cfset var qGetAllRecord = "" />
		<cfset var gridStruct = "" />
		<cfif len(arguments.searchString)>
			<cfset condition = condition & " -- your search conditions goes here" />
		</cfif>
		<cfstoredproc procedure="usp_Pagination" datasource="#application.DSN#" username="#application.DBUID#" password="#application.DBPWD#">
			<cfprocresult name="qGetAllRecord" resultset="1">
			<cfprocresult name="qtotalRecord" resultset="2">
			<cfprocparam dbvarname="@SqlColumns" cfsqltype="CF_SQL_VARCHAR" value="*">
			<cfprocparam dbvarname="@SqlFriendlyColumns" cfsqltype="CF_SQL_VARCHAR" value="*">
			<cfprocparam dbvarname="@SqlTableClause" cfsqltype="CF_SQL_VARCHAR" value="Jobs">
			<cfprocparam dbvarname="@StartRow" cfsqltype="CF_SQL_VARCHAR" value="#startRow#">
			<cfprocparam dbvarname="@EndRow" cfsqltype="CF_SQL_VARCHAR" value="#endRow#">
			<cfprocparam dbvarname="@SqlWhere" cfsqltype="CF_SQL_VARCHAR" value="#condition#">
			<cfprocparam dbvarname="@SqlRowNumOrderBy" cfsqltype="CF_SQL_VARCHAR" value="Jobs.createdDate desc">
			<cfprocparam dbvarname="@SqlOuterOrderBy" cfsqltype="CF_SQL_VARCHAR" value="createdDate desc">
		</cfstoredproc>
		<cfset gridStruct=StructNew() />
		<cfset gridStruct.query = qGetAllRecord />
		<cfset gridStruct.totalRowCount=qtotalRecord.countAll />
		<cfreturn qResult>
	</cffunction>
</cfcomponent>

The reason behind showing this code is to let you know the implementation of the server part.

In above function, I am returning fix number of records from my database. It is used to pull out ten ten records from a query that has thousands of records.

Now I’m calling this web service like:

http://localhost/cfc/mywebservice.cfc?returnformat=json&method=getJobs&currentPage=5&totalRecord=10

And it returns a data in JSON format like:

{“QUERY”:{“COLUMNS”:[“JOBID”,”JOBTITLE”,”CREATEDATE”,”JOBDETAIL”],”DATA”:[[“1″,”Title1″,”January, 25 2011 10:55:53″,”Detail is here”],[“2″,”Title2″,”January, 25 2011 10:55:53″,”Detail is here”],[“3″,”Title3″,”January, 20 2011 14:13:10″,”Detail is here”],[“4″,”Title4″,”January, 20 2011 14:13:10″,”Detail is here”],[“5″,”Title5″,”January, 20 2011 14:13:10″,”Detail is here”],[“6″,”Title6″,”January, 20 2011 14:13:10″,”Detail is here”],[“7″,”Title7″,”January, 18 2011 16:23:28″,”Detail is here”],[“8″,”Title8″,”January, 17 2011 11:22:02″,”Detail is here”],[“9″,”Title9″,”January, 11 2011 09:22:32″,”Detail is here”],[“10″,”Title10″,”January, 11 2011 09:22:32″,”Detail is here”]]},”TOTALROWCOUNT”:”120″}

Now we are ready to build Android app.

joblist.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 	android:layout_width="fill_parent"
 	android:layout_height="fill_parent"
	android:orientation="vertical">
 <EditText android:id="@+id/JobSearch"
 		android:layout_height="wrap_content"
		android:layout_width="fill_parent"
		android:singleLine="true"
 		android:hint="Search"/>
	<ListView android:id="@+id/JobList"
 		android:layout_width="fill_parent"
		android:layout_height="wrap_content" />
	<TextView android:id="@+id/EmptyJobList" android:layout_width="fill_parent"
		android:layout_height="fill_parent" android:text="No Results" android:visibility="invisible" />
</LinearLayout>

joblistheader.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="wrap_content">
	<TextView android:id="@+id/JobDate" android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:gravity="center"
		style="?android:attr/listSeparatorTextViewStyle" />
</LinearLayout>

joblistitem.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="wrap_content" android:paddingRight="5px">
	<TextView android:id="@+id/JobTitle" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_alignParentLeft="true"
		android:text="jobTitle" style="?android:attr/textAppearanceLarge" />
	<TextView android:id="@+id/JobDetail"
		android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>

And MyJobListView.java

package com.isummation.listview;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.TreeSet;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.json.JSONArray;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MyListView extends Activity {
	private static boolean isInitialized;
	private static int JOBID_IDX;
	private static int JOBTITLE_IDX;
	private static int CREATEDDATE_IDX;
	private static int JOBDETAIL_IDX;
	private JobListAdapter jobListAdapter;
	private long TotalRowCount;
	private int totalRecordPerCall = 20;
	private Date lastDate;
	private EditText JobSearch;
	private ListView list;

	class JobListRowData {
		boolean isHeader;
		Date jobCreationDate;
		int jobId;
		String jobTitle;
		String jobDetail;
	}

	class JobListAdapter extends BaseAdapter {
		private static final int TYPE_ITEM = 0;
		private static final int TYPE_SEPARATOR = 1;
		private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
		private LayoutInflater mInflater;
		private ArrayList mData = new ArrayList();
		private TreeSet mSeparatorsSet = new TreeSet();
		private int count = 0;
		private boolean isCleared = true;

		public JobListAdapter() {
			mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			initialize();
		}

		public void ParseJSONArray(JSONArray JQueryData) {
			int len = JQueryData.length();

			final TextView EmptyListView = (TextView) findViewById(R.id.EmptyJobList);

			if (TotalRowCount == 0) {
				EmptyListView.setVisibility(View.VISIBLE);
			} else {
				EmptyListView.setVisibility(View.INVISIBLE);
			}
			try {
				for (int i = 0; i &lt; len; i++) {

					Date tempDate = new Date(JQueryData.getJSONArray(i)
					.getString(CREATEDDATE_IDX));

					if (lastDate == null) {
						JobListRowData jobListRowData = new JobListRowData();
						jobListRowData.isHeader = true;
						jobListRowData.jobCreationDate = new Date(JQueryData.getJSONArray(i)
								.getString(CREATEDDATE_IDX));
						addSeparatorItem(jobListRowData);

						jobListRowData = new JobListRowData();
						jobListRowData.jobId = JQueryData.getJSONArray(i)
								.getInt(JOBID_IDX);
						jobListRowData.jobTitle = JQueryData.getJSONArray(i)
								.getString(JOBTITLE_IDX);
						jobListRowData.jobDetail = JQueryData.getJSONArray(i)
								.getString(JOBDETAIL_IDX);
						addItem(jobListRowData);

						lastDate = new Date(JQueryData.getJSONArray(i)
								.getString(CREATEDDATE_IDX));
					} else if (lastDate.getDate() == tempDate.getDate()
							&amp;&amp; lastDate.getMonth() == tempDate.getMonth()
							&amp;&amp; lastDate.getYear() == tempDate.getYear()) {
						JobListRowData jobListRowData = new JobListRowData();
						jobListRowData.jobId = JQueryData.getJSONArray(i)
								.getInt(JOBID_IDX);
						jobListRowData.jobTitle = JQueryData.getJSONArray(i)
								.getString(JOBTITLE_IDX);
						jobListRowData.jobDetail = JQueryData.getJSONArray(i)
								.getString(JOBDETAIL_IDX);
						addItem(jobListRowData);
					} else {
						JobListRowData jobListRowData = new JobListRowData();
						jobListRowData.isHeader = true;
						jobListRowData.jobCreationDate = new Date(JQueryData.getJSONArray(i)
								.getString(CREATEDDATE_IDX));
						addSeparatorItem(jobListRowData);
						lastDate = new Date(JQueryData.getJSONArray(i)
								.getString(CREATEDDATE_IDX));

						jobListRowData = new JobListRowData();
						jobListRowData.jobId = JQueryData.getJSONArray(i)
								.getInt(JOBID_IDX);
						jobListRowData.jobTitle = JQueryData.getJSONArray(i)
								.getString(JOBTITLE_IDX);
						jobListRowData.jobDetail = JQueryData.getJSONArray(i)
								.getString(JOBDETAIL_IDX);
						addItem(jobListRowData);
					}

				}
			} catch (Exception e) {
				Toast.makeText(getApplicationContext(), e.getMessage(),
						Toast.LENGTH_LONG).show();
				Log.e(e.getClass().getName(), e.getMessage(), e);
				TotalRowCount = 0;
			}
		}

		public void initialize() {
			isCleared = false;
			count = 0;
			lastDate = null;
			mSeparatorsSet.clear();
			mData.clear();
			JSONArray JQueryData = getJobs(1);
			ParseJSONArray(JQueryData);
			isCleared = true;
		}

		public void addMoreData() {
			if (jobListAdapter.count &lt; TotalRowCount &amp;&amp; isCleared) { 
				JSONArray JQueryData = getJobs((jobListAdapter.count + totalRecordPerCall)
						/ totalRecordPerCall + 1);
				ParseJSONArray(JQueryData);
			}
		}

		public void addItem(final JobListRowData item) {
			mData.add(item);
			count += 1;
			notifyDataSetChanged();
		}

		public void addSeparatorItem(final JobListRowData item) {
			mData.add(item);
			mSeparatorsSet.add(mData.size() - 1);
			notifyDataSetChanged();
		}

		public int getItemViewType(int position) {
			return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR
					: TYPE_ITEM;
		}

		public int getViewTypeCount() {
			return TYPE_MAX_COUNT;
		}

		public int getCount() {
			return mData.size();
		}

		public JobListRowData getItem(int pos) {
			return mData.get(pos);
		}

		public long getItemId(int pos) {
			return pos;
		}

		@Override
		public boolean isEnabled(int position) {
			return mSeparatorsSet.contains(position) ? false : true;
		}

		public View getView(int position, View convertView, ViewGroup parent) {
			ViewHolder holder;
			int type = getItemViewType(position);
			if (convertView == null) {
				holder = new ViewHolder();
				switch (type) {
				case TYPE_ITEM:
					convertView = mInflater.inflate(R.layout.joblistitem, null);
					holder.text = (TextView) convertView
							.findViewById(R.id.JobTitle);
					holder.text2 = (TextView) convertView
							.findViewById(R.id.JobDetail);
					break;
				case TYPE_SEPARATOR:
					convertView = mInflater.inflate(R.layout.joblistheader,
							null);
					holder.text = (TextView) convertView
							.findViewById(R.id.JobDate);
					break;
				}
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}
			switch (type) {
			case TYPE_ITEM:
				holder.text.setText(mData.get(position).jobTitle);
				holder.text2.setText(mData.get(position).jobDetail);
				break;
			case TYPE_SEPARATOR:
				try {
					holder.text
							.setText(android.text.format.DateFormat.format(
									"MMM dd, yyyy",
									mData.get(position).jobCreationDate));
				} catch (Exception e) {
					Toast.makeText(getApplicationContext(), e.getMessage(),
							Toast.LENGTH_LONG).show();
					Log.e(e.getClass().getName(), e.getMessage(), e);
				}
				break;
			}
			return convertView;
		}

		class ViewHolder {
			TextView text;
			TextView text2;
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.joblist);

		JobSearch = (EditText) findViewById(R.id.JobSearch);
		JobSearch.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
		JobSearch.setOnKeyListener(new View.OnKeyListener() {

			public boolean onKey(View v, int keyCode, KeyEvent event) {
				if (keyCode == KeyEvent.KEYCODE_ENTER
						&amp;&amp; event.getAction() == KeyEvent.ACTION_DOWN) {
					jobListAdapter.initialize();
				}
				return false;
			}
		});

		jobListAdapter = new JobListAdapter();

		list = (ListView) findViewById(R.id.JobList);
		list.setAdapter(jobListAdapter);
		list.setOnScrollListener(new AbsListView.OnScrollListener() {

			public void onScrollStateChanged(AbsListView view, int scrollState) {

			}

			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				boolean loadMore = firstVisibleItem + visibleItemCount &gt;= totalItemCount;
				if (loadMore) {
					jobListAdapter.addMoreData();
				}
			}
		});

		list.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<!--?--> parent, View view,
					int position, long id) {
				JobListRowData item = jobListAdapter.getItem(position);
				if (!item.isHeader) {
					// start another activity
				}
			}
		});
	}

	public JSONArray getJobs(int currentPage) {
		try {
			String searchString = JobSearch.getText().toString();
			HttpClient httpClient = new DefaultHttpClient();
			HttpContext localContext = new BasicHttpContext();
			HttpGet httpGet = new HttpGet(
					"http://10.0.2.2/cfc/iphonewebservice.cfc?returnformat=json&amp;method=getJobs¤tPage="
							+ URLEncoder.encode("" + currentPage, "UTF-8")
							+ "&amp;totalRecord="
							+ totalRecordPerCall
							+ "&amp;searchString="
							+ URLEncoder.encode(searchString, "UTF-8"));
			HttpResponse response = httpClient.execute(httpGet, localContext);

			BufferedReader reader = new BufferedReader(new InputStreamReader(
					response.getEntity().getContent(), "UTF-8"));
			String sResponse = reader.readLine();
			JSONObject JResponse = new JSONObject(sResponse);
			JSONObject JQuery = JResponse.getJSONObject("QUERY");
			TotalRowCount = JResponse.getLong("TOTALROWCOUNT");
			JSONArray JQueryColumns = JQuery.getJSONArray("COLUMNS");

			if (!isInitialized) {
				int len = JQueryColumns.length();
				for (int i = 0; i &lt; len; i++) {
					if (JQueryColumns.getString(i).equals("JOBID")) {
						JOBID_IDX = i;
					} else if (JQueryColumns.getString(i).equals("JOBTITLE")) {
						JOBTITLE_IDX = i;
					} else if (JQueryColumns.getString(i).equals("CREATEDATE")) {
						CREATEDDATE_IDX = i;
					} else if (JQueryColumns.getString(i).equals(
							"JOBDETAIL")) {
						JOBDETAIL_IDX = i;
					}
				}
				isInitialized = true;
			}

			return JQuery.getJSONArray("DATA");
		} catch (Exception e) {
			Toast.makeText(getApplicationContext(), e.getMessage(),
					Toast.LENGTH_LONG).show();
			Log.e(e.getClass().getName(), e.getMessage(), e);
			finish();
			return new JSONArray();
		}
	}
}

Update:

I’ve updated above code, for “Loading…” effect while getting next 20 records.
You can now download the source code.
Download source code

Binding ColdFusion query to DataGrid in Flex


Hi,
In this post I’ll explain you how to bind ColdFusion data into Flex grid.
First, get ready with your cfc.
Your cfc function may look like:

<cffunction name="getCategoryList" access="remote" output="false" returntype="query">
<cfset var qCategoryList = "" />
	<cfquery name="qCategoryList" datasource="#application.dsn#" username="#application.dbuid#" password="#application.dbpwd#">
		SELECT
			categoryId,
			categoryName, 
			Description
		FROM	tblCategory
	</cfquery>
	<cfreturn qCategoryList />
</cffunction>

Please note here that we are returning a ColdFusion query.

Now I’m setting up mxml page to show the grid and fetch the ColdFusion data with RemoteObject.

<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
		 xmlns:s="library://ns.adobe.com/flex/spark" 
		 xmlns:mx="library://ns.adobe.com/flex/mx" 
		 creationComplete="categoryGateway.getAllForGrid();">
	<fx:Declarations>
		<s:RemoteObject destination="ColdFusion" source="cfc.categoryGateway" id="categoryGateway">
			<mx:method name="getAllForGrid" result="returnHandler(event)"
					   fault="mx.controls.Alert.show(event.fault.faultString)"/>
		</s:RemoteObject>
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.rpc.events.ResultEvent;
			private function returnHandler(e:ResultEvent):void 
			{
				dgCategory.dataProvider = e.result;
			}
		]]>
	</fx:Script>
	<s:VGroup>
		<mx:DataGrid id="dgCategory">
			<mx:columns>
				<mx:DataGridColumn headerText="Category ID" dataField="categoryId"/>
				<mx:DataGridColumn headerText="Category Name" dataField="categoryName"/>
				<mx:DataGridColumn headerText="Description" dataField="Description"/>
			</mx:columns>
		</mx:DataGrid>
	</s:VGroup>
</s:Panel>

Easy enough?

Now let me point out important notes:

Remember one thing that ActionScript 2.0 and 3.0 are now case sensitive!

So if you write “description” instead of “Description” in DataGridColumn, it wouldn’t bind!

Also If your cfc method returns a ColdFusion structure, then you can get the value of structure elements by typing element name in capital later.

For e.g.

Your cfc method:

<CFSET var myStruct = structNew()>
<CFSET myStruct.query = qGetAllRecord>
<CFSET myStruct.RowCount = qGetAllRecord.recordCount>
<CFRETURN myStruct>

And your ActionScript code would be:

dgCategory.dataProvider = e.result.QUERY;
Alert.show(e.result.ROWCOUNT);

Automated tests using Ant integration of testing frameworks – cfunit, cfcunit and mxunit


If you have seen my previous post, Then you might want to automate testing. You can automate all three popular testing frameworks (cfunit, cfcunit and mxunit) with ant.

I’ve tested in eclipse 3.6 version, but it will also work in Adobe ColdFusion Builder.

Step 1: Create build.xml file and put it in your project.

Step 2: Copy and paste the following code in build.xml file.

For mxunit :

<?xml version="1.0"?>

<project name="mxunit_example" default="main" basedir=".">
	<taskdef name="mxUnit" classname="org.mxunit.ant.MXUnitAntTask" />

	<property name="domain" value="localhost"/>
	<property name="webroot" value="c:\inetpub\wwwroot"/>

	<target name="main">
		<mxUnit server="${domain}"
			defaultrunner="/mxunit/runner/HttpAntRunner.cfc"
			verbose="true">
			<!-- componentPath is not required, but it results in a much faster test run -->
			<directory remoteMethod="run" path="${webroot}\mxunit_example" packageName="mxunit_example" componentPath="mxunit_example" recurse="true" includes="*.cfc"/>
		</mxUnit>
	</target>
</project>

For cfunit :

<?xml version="1.0"?>
<project name="CFUnitExample" default="main" basedir=".">
	<taskdef name="CFUnit" classname="net.sourceforge.cfunit.ant.CFUnit"/>

	<property name="domain" value="http://localhost/"/>

	<target name="main">
		<CFUnit testcase="${domain}CFUnitExample/test/MyCFCTest.cfc"
			verbose="true"/>
	</target>
</project>

For cfcunit :

<?xml version="1.0"?>

<project name="CFCUnit_example" default="main" basedir=".">
	<taskdef name="CFCUnit" classname="org.cfcunit.ant.CFCUnitTask" />

	<property name="domain" value="localhost"/>

	<target name="main">
		<CFCUnit hostname="${domain}"
			testcase="CFCUnit_example.CalculatorTest"
			verbose="true"
			haltonfailure="true"
			haltonerror="true"
			showstacktrace="true"/>
	</target>
</project>

Explanation :

1) Project name should match with you project name.

2) Class name of the taskdef: You should have put class file on the correct location. For e.g. org.mxunit.ant.MXUnitAntTask

I have the MXUnitAntTask.class file in c:\inetpub\wwwroot\org\mxunit\ant\ location.

If you don’t have that class file then download the mxunit, extract it, extract the jar file located in mxunit\ant\lib\mxunit-ant.jar. You will find the class files in that.

Step 3: Configure Ant for you project

1) Right click on your project and go to properties -> Builders. Click on new button.

2) Select Ant builder. Press ok and it will open a dialog (edit configuration). Type a new build name.

3) Select your build.xml file for build file by browse the workspace.

4) Select class path tab and click on “add external jars” button. Link your mxunit-ant.jar file.

5) Select Build option tab and click the check box for “Specify working set of relevant resources”.

6) Click on “Specify Resources” button and select your project or you can also select single directory. Click on ok button.

If you find the difficulties in above steps then please visit fusion authority website.

Cheers! Now write test cases. Each time when you hit save, eclipse will automatically build you files and you will see outputs in console.

Useful links :

http://blog.stannard.net.au/2006/09/23/cfcunit-and-ant/

http://corfield.org/blog/post.cfm/Automated_Testing_with_cfcUnit

http://www.fusionauthority.com/techniques/4568-test-driven-development-with-coldfusion-part-ii.htm

Testing frameworks – cfunit, cfcunit and mxunit


Hi folks,

There are lots of testing frameworks available for Test-driven development (TDD).
Most of them are of xUnit family. Recently I had gone through three most popular
testing frameworks for ColdFusion.

CFUnit

CFCUnit

MXUnit

After using all three, I decided to post a comparison of them. Information may be inaccurate, Corrections are always acceptable.

Features CFUnit CFCUnit MXUnit
Based on XUnit & JUnit Yes Yes Yes
Eclipse Plug-in Removed in final cfeclipse release No Yes
Eclipse Ant integration using jar Yes Yes Yes
Supported Output format HTML HTML, XML HTML,XML,JUnit Report XML, CF query, CF Array
HTML UI No Yes Yes
Easy to see your data with cfoutput, cfdump, and debug(), also cfcatch to view data No No Yes
Directory runner No No Yes
Testing private methods Yes Yes
Support for setUp() and tearDown() functions Yes Yes Yes
beforeTests() and afterTests() functions No No Yes
Help and tutorials availability Few Few Enough
Assertion Only Standard Assertions More than 30 Built-in assertion There are around 10 built-in assertions

I personally like MXUnit for its eclipse
Plug-in and better UI.