您应该对什么进行单元测试? –测试技术3
他使用的原因之一是有些測試似乎毫無意義,這使我想到了什么是單元測試,什么也不需要打擾。
考慮下面一個簡單的不可變的Name Bean,其中包含一個構造函數和一堆getter。
在這個示例中,我將讓代碼說明一切,因為我希望任何測試都是毫無意義的。
public class Name {private final String firstName;private final String middleName;private final String surname;public Name(String christianName, String middleName, String surname) {this.firstName = christianName;this.middleName = middleName;this.surname = surname;}public String getFirstName() {return firstName;}public String getMiddleName() {return middleName;}public String getSurname() {return surname;} }……只是為了強調這一點,這是毫無意義的測試代碼:
public class NameTest {private Name instance;@Beforepublic void setUp() {instance = new Name("John", "Stephen", "Smith");}@Testpublic void testGetFirstName() {String result = instance.getFirstName();assertEquals("John", result);}@Testpublic void testGetMiddleName() {String result = instance.getMiddleName();assertEquals("Stephen", result);}@Testpublic void testGetSurname() {String result = instance.getSurname();assertEquals("Smith", result);} }測試此類毫無意義的原因是該代碼不包含任何邏輯。 但是,當您向Name類添加如下內容時:
public String getFullName() {if (isValidString(firstName) && isValidString(middleName) && isValidString(surname)) {return firstName + " " + middleName + " " + surname;} else {throw new RuntimeException("Invalid Name Values");}}private boolean isValidString(String str) {return isNotNull(str) && str.length() > 0;}private boolean isNotNull(Object obj) {return obj != null;}…然后整個情況發生了變化。 以if語句的形式添加一些邏輯會生成大量測試:
@Testpublic void testGetFullName_with_valid_input() {instance = new Name("John", "Stephen", "Smith");final String expected = "John Stephen Smith";String result = instance.getFullName();assertEquals(expected, result);}@Test(expected = RuntimeException.class)public void testGetFullName_with_null_firstName() {instance = new Name(null, "Stephen", "Smith");instance.getFullName();}@Test(expected = RuntimeException.class)public void testGetFullName_with_null_middleName() {instance = new Name("John", null, "Smith");instance.getFullName();}@Test(expected = RuntimeException.class)public void testGetFullName_with_null_surname() {instance = new Name("John", "Stephen", null);instance.getFullName();}@Test(expected = RuntimeException.class)public void testGetFullName_with_no_firstName() {instance = new Name("", "Stephen", "Smith");instance.getFullName();}@Test(expected = RuntimeException.class)public void testGetFullName_with_no_middleName() {instance = new Name("John", "", "Smith");instance.getFullName();}@Test(expected = RuntimeException.class)public void testGetFullName_with_no_surname() {instance = new Name("John", "Stephen", "");instance.getFullName();}因此,鑒于我剛剛說過,您不需要測試不包含任何邏輯語句的對象,并且在邏輯 語句列表中,我將包含if并與所有運算符一起切換 (+-*- ),以及可能發生變化和對象狀態的一整套事物。
在此前提下,我建議在我前兩篇博客中一直在討論的Address項目中為地址數據訪問對象(DAO)編寫單元測試毫無意義。 DAO由AddressDao接口定義,并由JdbcAddress類實現:
public class JdbcAddress extends JdbcDaoSupport implements AddressDao {/*** This is an instance of the query object that'll sort out the results of* the SQL and produce whatever values objects are required*/private MyQueryClass query;/** This is the SQL with which to run this DAO */private static final String sql = "select * from addresses where id = ?";/*** A class that does the mapping of row data into a value object.*/class MyQueryClass extends MappingSqlQuery<address> {public MyQueryClass(DataSource dataSource, String sql) {super(dataSource, sql);this.declareParameter(new SqlParameter(Types.INTEGER));}/*** This the implementation of the MappingSqlQuery abstract method. This* method creates and returns a instance of our value object associated* with the table / select statement.* * @param rs* This is the current ResultSet* @param rowNum* The rowNum* @throws SQLException* This is taken care of by the Spring stuff...*/@Overrideprotected Address mapRow(ResultSet rs, int rowNum) throws SQLException {return new Address(rs.getInt("id"), rs.getString("street"),rs.getString("town"), rs.getString("post_code"),rs.getString("country"));}}/*** Override the JdbcDaoSupport method of this name, calling the super class* so that things get set-up correctly and then create the inner query* class.*/@Overrideprotected void initDao() throws Exception {super.initDao();query = new MyQueryClass(getDataSource(), sql);}/*** Return an address object based upon it's id*/@Overridepublic Address findAddress(int id) {return query.findObject(id);}}在上面的代碼中,接口中的唯一方法是:
@Overridepublic Address findAddress(int id) {return query.findObject(id);}……這實際上是一種簡單的吸氣方法。 在我看來,這是可以的,因為DAO中確實不應包含屬于AddressService的任何業務邏輯,該業務邏輯應具有大量的單元測試。
您可能要決定是否要為MyQueryClass編寫單元測試。 對我來說這是一個臨界情況,所以我期待任何評論……
我猜想有人會不同意這種方法,說您應該測試JdbcAddress對象,這是真的,我親自為其編寫了一個集成測試,以確保我使用的數據庫可以正常運行,并且可以理解我的數據庫SQL,并且兩個實體(DAO和數據庫)可以互相通信,但是我不會打擾對其進行單元測試。
總而言之,單元測試必須是有意義的,并且對“有意義”的良好定義是被測對象必須包含一些獨立的邏輯。
參考: 您應該對什么進行單元測試? – Captain Debug博客上的 JCG合作伙伴 Roger Hughes的 測試技術3
相關文章 :
- 測試技巧–不編寫測試
- 端到端測試的濫用–測試技術2
- 常規單元測試和存根–測??試技術4
- 使用模擬的單元測試–測試技術5
- 為舊版代碼創建存根–測試技術6
- 有關為舊版代碼創建存根的更多信息–測試技術7
- 為什么要編寫單元測試–測試技巧8
- 一些定義–測試技術9
- 使用FindBugs產生更少的錯誤代碼
- 在云中開發和測試
翻譯自: https://www.javacodegeeks.com/2011/11/what-should-you-unit-test-testing.html
總結
以上是生活随笔為你收集整理的您应该对什么进行单元测试? –测试技术3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java并发教程–阻塞队列
- 下一篇: Oracle JRockit Missi