Каков наилучший способ модульного тестирования cfc, который использует Java-объект для большей функциональности?
У меня есть cfc, который в значительной степени опирается на объект Java (созданный с помощью JavaLoader) для большинства его основных функций, для которых я хотел бы написать несколько тестов, и я не уверен, что лучший способ сделать это. Вот пример метода, для которого я хотел бы написать тест, в котором instance.note является java-объектом.
<cffunction name="getNotes" returntype="Array" access="public" output="false" hint="I return a list of a users notebooks" >
<cfargument name="maxCount" type="numeric" required="false" default="9999" hint="The maximum number of notes to get" />
<cfscript>
if(arguments.maxCount)
return instance.note.listNotes(maxCount);
else
return instance.note.listNotes();
</cfscript>
</cffunction>
Одна вещь, о которой я подумал, - это создать CFC-заглушку с такими же именами методов и схожими возвращаемыми значениями, а затем высмеивать эту заглушку и вставлять ее?
3 ответа
Когда нам нужно было выполнить модульное тестирование функций CF, которые основывались на Java-объектах (которых мы сделали МНОГО), мы использовали Mockito для имитации Java-объектов.
Итак, надеясь, что этот фрагмент кода имеет смысл, прошло уже почти год с тех пор, как я это сделал:
<cfcomponent displayname="TestWhatever" extends="mxunit.framework.TestCase" output="false">
<cffunction name="setUp" access="public" returntype="void">
<cfscript>
// named it mk for keeping it short
variables.mk = createObject("java","org.mockito.Mockito");
//Create the mock object
variables.mockNote = mk.mock(createObject("java","com.company.whatever.note").getClass());
// Mock Data
fullList = {whatever listNotes() returns}
partialList3 = {whatever listNotes(3) returns}
//some common mocking
mk.when(variables.mockNote.listNotes()).thenReturn(fullList);
mk.when(variables.mockNote.listNotes(mk.eq(3))).thenReturn(partialList3);
mk.when(variables.rootOrgObj.guid()).thenReturn("root");
// Assign the mock object to where your CFC expects it.
instance.note = variables.mockNote
</cfscript>
</cffunction>
</cfcomponent>
Сказав это, если ваша функция выборки реальна, нет смысла ее тестировать. Это просто ничего не делать, но быть прокси для объекта Java. Там нет существенной логики для тестирования.
Поскольку у вас есть значение по умолчанию для cfargument, оно всегда будет существовать (опять же, я думаю, это был год, так как я сделал CF), поэтому ваше защитное утверждение даже не требуется - всегда будет вызываться первый путь кода, с переданным maxCount, если указано, или с 9999, если нет.
Разве вы не можете просто написать значимые утверждения о результате, то есть о массиве заметок? Глядя на этот код, единственное, что я проверю, это: а) когда вы передаете maxCount, соответствует ли ваш результирующий массив этому размеру? б) без maxCount, список заметок длина, которую вы ожидаете? Потому что это все, что делает ваш код. Я бы протестировал ваш код, а не код базового Java-объекта.
Я взял ответ Эдварда и реализовал его так:
Я использовал библиотеку JavaLoader для создания моего объекта mockito. variables.cfEvernote = "";
variables.classLoader = createObject("component", "resources.JavaLoader").
init(["#expandPath('../lib/mockito-all-1.8.5.jar')#",
"#expandPath('../lib/CFEvernote.jar')#",
"#expandPath('../lib/libthrift.jar')#",
"#expandPath('../lib/evernote-api-1.18.jar')#"]);
variables.mockito = variables.classLoader.create("org.mockito.Mockito").init();
Затем в методе настройки моего теста munit я создаю свой новый фиктивный объект Java:
<cffunction name="setUp" access="public" output="false" returntype="void">
<cfscript>
variables.cfEvernote = createObject("component","com.714studios.cfevernote.CFEvernote").
Init(variables.configArray[1],variables.configArray[2],
"sandbox.evernote.com",
"http://localhost/cfevernote/callback.cfm"
"#ExpandPath('../lib')#");
variables.mockCFEvernote = variables.mockito.mock(variables.classLoader.create("com.sudios714.cfevernote.CFEvernote").
Init("123","S1","232","sandbox.evernote.com","mock").getClass());
variables.cfEvernote.setCFEvernote(mockCFEvernote);
</cfscript>
Затем в своих тестах я создаю свое ложное поведение вот так.
<cffunction name="test..." returntype="void" access="public" output="false" >
<cfscript>
var notebooks = "";
var expected = 12;
var i = 0;
var retArray = createObject("Java","java.util.ArrayList");
var actual = "";
for(i = 1; i lte 12; i = i + 1){
retArray.Add("");
}
variables.mockito.when(mockCFEvernote.listNotebooks(12)).thenReturn(retArray);
notebooks = variables.cfEvernote.getNotebooks(12);
actual = arrayLen(notebooks);
assertEquals(expected,actual);
</cfscript>
Я также написал об этом чуть более подробно здесь - http://blog.bittersweetryan.com/2011/07/unit-testing-coldfusion-components-that.html.