JUnit來測試Applicaiton.
Model的連帶處理考量
從上回簡單的訊息表示Model(MsgData),這次我們再加上一個新的使用者情報Model(PersonData),這樣子顯示訊息時
不僅可以顯示使用者名稱,通常是使用SQL的JOIN句子,來互相關連,不過在Play!裡是使用JPA,Data並不是SQL table,
都是作為物件永續化管理,Model的關聯也是被設計成物件和物件的互相關聯。
首先在「models」裡作成PersonData類別
package models;
import java.util.*;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import play.db.jpa.Model;
@Entity
public class PersonData extends Model {
public String name;
public String mail;
@OneToMany
public List<MsgData> messages;
public PersonData(String name,String mail){
this.name = name;
this.mail = mail;
this.messages = new ArrayList<MsgData>();
}
}
在這裡除了name和mail兩個欄位以外,還有保管MsgData實例的List欄位,這個List就是用來整合關聯MsgData物件。
這裡一定要注意的是「@OneToManay宣告」,JPA裡物件的永續化時,如果沒加上@OneToMany的話,就無法達成永續化。
藉由加上@OneToManay,這就告訴JPA這個欄位成了一對多對應的物件參照欄位。
MsgData的修正
接下來MsgData裡使用PersonData來取代name
package models;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import play.db.jpa.Model;
@Entity
public class MsgData extends Model {
private static final long serialVersionUID = 1L;
public String message;
@ManyToOne
public PersonData person;
public Date time = Calendar.getInstance().getTime();
public MsgData(String message,PersonData person){
this.person = person;
this.message = message;
person.messages.add(this);
}
}
在這裡面有保管PersonData實例的person欄位,並加上了「@ManyToOne」的宣告,這樣一來就能夠將MsgData永續化。
new的時候,傳入引數裡的PersonData實例,在設定person屬性的同時,呼叫出person的message.add,在person
這邊將訊息保管起來。
PersonData的Controller
接下來作成PersonData的Controller,在「controller」資料夾裡做一個新的「Person.java」檔案。
這次就先加入add和index兩個基本的方法。
package controllers;
import java.util.List;
import play.mvc.Controller;
import models.*;
public class Person extends Controller {
public static void index(String name) {
List<PersonData> datas = null;
datas = PersonData.findAll();
render(datas);
}
public static void add(String name,String mail){
String msg = "請輸入資料。";
if (request.method.equals("POST")) {
PersonData person = new PersonData(name,mail);
person.save();
msg = "資料已經被保存了";
}
render(msg);
}
}
準備PersonData用的Template
接下來在「views」資料夾裡做一個新的「Person」資料夾,然後加入兩個檔案「index.html」「add.html」
#index.html的程式碼
#{extends 'main.html' /}
#{set title:'Home' /}
<h1>表示訊息</h1>
#{list items:datas, as:'data'}
<li>${data.id}: ${data.name}(${data.mail})</li>
#{/list}
#add.html的程式碼
#{extends 'main.html' /}
#{set title:'Home' /}
<h1>作成訊息</h1>
${msg}
<form method="post" action="@{Person.add}">
<table>
<tr><td>name:</td><td><input type="text" id="name" name="name" /></td></tr>
<tr><td>mail:</td><td><input type="text" id="mail" name="mail" /></td></tr>
<tr><td></td><td><input type="submit" value="送出" /></td></tr>
</table>
</form>
接下來修正MsgData的Controller
package controllers;
import java.util.List;
import models.*;
import play.mvc.Controller;
public class Application extends Controller {
public static void index(String name) {
List<MsgData> datas = null;
datas = MsgData.findAll();
render(datas);
}
public static void add(String message,String name){
String msg = "請輸入資料。";
if (request.method.equals("POST")) {
PersonData person = PersonData.find("name",name).first();
if (person != null){
MsgData data = new MsgData(message,person);
data.save();
person.save();
msg = "資料已經被保存了。";
} else {
msg = "個人資料尚未登入。";
}
}
render(msg);
}
}
作成MsgData用的Template
#index.html的原始碼
#{extends 'main.html' /}
#{set title:'Home' /}
<h1>表示訊息</h1>
#{list items:datas, as:'data'}
<li>${data.id}: ${data.message}(${data.person.name}, ${data.person.mail})</li>
#{/list}
#add.html的原始碼
#{extends 'main.html' /}
#{set title:'Home' /}
<h1>作成訊息</h1>
${msg}
<form method="post" action="@{Application.add}">
<table>
<tr><td>name:</td><td><input type="text" id="name" name="name" /></td></tr>
<tr><td>message:</td><td><input type="text" id="message" name="message" /></td></tr>
<tr><td></td><td><input id="submit" type="submit" value="送出" /></td></tr>
</table>
</form>
顯示使用者投稿的全部訊息
從MsgData裡取得PersonData的@ManyToOne處理已經明白了之後。接下來利用看看@OneToMany關聯的實例。
稍微修正剛才Person的index.html,將它改成可以顯示所有使用者的訊息。
#{extends 'main.html' /}
#{set title:'Home' /}
<h1>表示訊息</h1>
#{list items:datas, as:'data'}
<li>${data.id}: ${data.name}(${data.mail})
<ul>
#{list items:data.messages, as:'message'}
<li>${message.message}</li>
#{/list}
</ul>
</li>
#{/list}
完全不需要改動到Controller,在#{list items:datas, as:'data'}的重複操作中,又重複操作
#{list items:data.messages, as:'message'}來取出PersonData裡的messages全部元素。
有關測試
在Play!裡備有JUnit的標準測試機能,在作成的專案裡都會有「test」這個資料夾,這個資料夾就是為了要配置測試用的
程式碼的地方。裡面有標準的「ApplicationTest.java」「BasicTest.java」「Application.test.html」等名稱
的程式碼檔案。這是在專案作成時自動生成的測試用程式碼,當然也可以修正這些檔案來做自己想要的測試。
在Play!裡有三種測試種類。「UnitTest(單元測試)」「FunctionalTest(功能測試)」「SeleniumTest」。
想要實行測試模式時,只要輸入
play test sampleapp
然後測試下面的網址
http://localhost:9000/@tests
檢查UnitTest程式碼
首先先看實行UnitTest的「BasicTest.java」檔案
import org.junit.*;
import java.util.*;
import play.test.*;
import models.*;
public class BasicTest extends UnitTest {
@Test
public void aVeryImportantThingToTest() {
assertEquals(2, 1 + 1);
}
}
在這裡「assertEquals」方法傳入兩個引數比較是否相等。這個UnitTest類別是直接繼承JUnit的org.junit.Assert類別,
並直接使用JUnit的assert方法。
檢查FunctionalTest程式碼
import org.junit.*;
import play.test.*;
import play.mvc.*;
import play.mvc.Http.*;
import models.*;
public class ApplicationTest extends FunctionalTest {
@Test
public void testThatIndexPageWorks() {
Response response = GET("/");
assertIsOk(response);
assertContentType("text/html", response);
assertCharset("utf-8", response);
}
}
接下來在FunctionalTest裡加上一些各自的定義。頁面的存取已經在上面的範例測試過了,這次就定義測試Model的方法。
@Before
public void setup() {
Fixtures.deleteAll();
}
@Test
public void testModelData() {
PersonData p = new PersonData("測試", "test@test.com");
p.save();
MsgData m = new MsgData("這是測試。", p);
m.save();
p.messages.add(m);
p.save();
assertNotNull(p);
assertNotNull(m);
assertEquals(p.name, m.person.name);
assertEquals(m.id,p.messages.get(0).id);
List<PersonData> plist = PersonData.find("name", "測試").fetch();
assertEquals(1, plist.size());
List<MsgData> mlist = MsgData.find("person", plist.get(0)).fetch();
assertEquals(1, mlist.size());
}
在這裡作成PersonData和MsgData,首先作成PersonData實例並保存,接下來將訊息和PersonData作為
引數作成MsgData實例,然後確認p和m的依存關係。assertNotNull確認引數是不是null。
assertEquals確認PersonData和MsgData的相同參照物件的name和id是否是參照到正確的物件。
這個測試之前,有一個setup的方法,並使用了「@Before」的宣告,這是表示說這是在測試前的前置動作,這裡用「Fixtures.deleteAll();」
就是將之前測試用的資料全部刪除、在每一個測試開始之前基本上都要先像這樣將資料初始化。
透過data.yml來生成測試資料
如果像之前生成資料、確認資料都還要寫一堆程式碼,在生成大量資料的時候就變得很麻煩。而且當要改變資料內容來驗證時
又還需要重新更改程式碼,如果可以的話,希望能夠把Model和程式碼切離。
在Play!的情況,「test」資料夾裡有一個「data.yml」檔案,紀錄了Model相關的情報,藉由這個來自動生成資料是可能的。
PersonData(tuyano): name: tuyano mail: tuyano@mac.com PersonData(taro): name: taro mail: taro@yamada.com MsgData(msg1): message: Hello person: tuyano MsgData(msg2): message: Good-bye person: taro
把data.yml打開,然後以這樣的格式紀錄Model內容
Model名稱 ( 實例名稱 ):
屬性名稱: 値
屬性名稱: 値
……以下略……
屬性名稱: 値
屬性名稱: 値
……以下略……
測試用的程式碼
@Test
public void testFullData() {
Fixtures.load("data.yml");
assertEquals(2, PersonData.count());
assertEquals(2, MsgData.count());
}
SeleniumTest的原始碼
SeleniumTest是在以HTML為基礎的Template檔案裡嵌入特殊的標籤來實行的。打開「Application.test.html」就能知道了。
#{selenium}
open('/')
waitForPageToLoad(1000)
assertNotTitle('Application error')
#{/selenium}
使用seleniumTest測試add頁面
在Application.test.html裡加入
#{fixture delete:'all', load:'data.yml' /}
#{selenium}
open('/add')
waitForPageToLoad(1000)
type('name','test')
type('message','*** this is selenium test.***')
clickAndWait('submit')
#{/selenium}
參考文章:http://codezine.jp/article/detail/4752
No comments:
Post a Comment