This is the third (or fourth) in a series about recent discoveries in testing and mocking. This challenge was to accept two numbers and use them to query a database to get the result. Is this a real world example? Maybe not for math but obviously we use DBs all the time so that part is real.
The function starts off simple enough.
We're still accepting the two numbers. then we run into the crux of the issue which is how are we to we provide the results of a query without actually calling the DB which may or may not exist in our testing environment. This will not work:
Since the query originates from the function we have no way of controlling its contents except to provide a db. The only way that anything can exists from the outside of a function is to be passed in and that's only done through
a) a parameter in a function (like numA and numB are) or
b) as a property of the CFC itself.
I chose this latter option and adapted the CFC to look like this:
Some key points here are in the "setAttributes()" line where we set a "sourceQuery" attribute. Since a query of queries refers to the name of the source query and not the query itself, this was the way to do that. Thanks to Chris Tierny (@CFJSGeek) and this post from 2011.
The function starts off simple enough.
public function mathFromQuery(numeric numA,numeric numB){
}
We're still accepting the two numbers. then we run into the crux of the issue which is how are we to we provide the results of a query without actually calling the DB which may or may not exist in our testing environment. This will not work:
public function mathFromQuery(numeric numA,numeric numB){
local.qs=new query(datasource=blah,sql="select * from blah where blah blah blah");
}
Since the query originates from the function we have no way of controlling its contents except to provide a db. The only way that anything can exists from the outside of a function is to be passed in and that's only done through
a) a parameter in a function (like numA and numB are) or
b) as a property of the CFC itself.
I chose this latter option and adapted the CFC to look like this:
component accessors="true"{ property name="dataQ" type="query";
public function mathFromQuery(numeric numA,numeric numB){
}
}From there it was (almost) trivial to set up the rest of the method but how would this property get set in production? Either at instantiation or via dependency injection. This might not be the more real world approach to a query but this is all about exploring techniques which we can mix and match later.
public function mathFromQuery(numeric numA,numeric numB){ local.qc=new query(dbtype="query",
sql="select answer from sourceQuery where numA=:numA and numB=:numB"); local.qc.addParam(name="numA",value=arguments.numA); local.qc.addParam(name="numB",value=arguments.numB); local.qc.setAttributes(sourceQuery=this.getDataQ()); local.res=local.qc.execute().getresult(); return local.res.answer[1]; }
Some key points here are in the "setAttributes()" line where we set a "sourceQuery" attribute. Since a query of queries refers to the name of the source query and not the query itself, this was the way to do that. Thanks to Chris Tierny (@CFJSGeek) and this post from 2011.
Creating the Test
Here are some of the key points of this.
- We have yet to use Mockbox in any of this and this wasn't an exception
- We instantiate the object and then use queryNew to make the data we need. This does present one difficulty going forward which is going to be making sure that this schema matches the actual one in production (since everyone uses a db to do math, right?)
- We can use the queryNew to create multiple scenarios. We have the main one in the beforeAll() but notice how we can manipulate the query in the second it() just to make sure we are using the submitted query to get our result.
component extends="testbox.system.BaseSpec"{ /*********************************** LIFE CYCLE Methods ***********************************/ // executes before all suites+specs in the run() method function beforeAll(){ testobj=createObject("component","testmods.first.second.third.vanillaCFC"); local.q=queryNew("numA,numB,answer","int,int,int",[{"numA":8,"numB":9,"answer":72}]); testobj.setDataQ(q); testme=testobj.mathFromQuery(8,9); } // executes after all suites+specs in the run() method function afterAll(){ } /*********************************** BDD SUITES ***********************************/ function run(){ describe( "My test Suite", function(){ it( "be a number", function(){
expect( testme ).toBeTypeOf('numeric'); }); it( "should be boo", function(){ local.q=queryNew("numA,numB,answer","int,int,int",[{"numA":8,"numB":9,"answer":"boo"}]); testobj.setDataQ(q); testme=testobj.mathFromQuery(8,9); expect( testme ).toBe('boo'); }); }); } }
Comments
Post a Comment