วิธีทดสอบ data access layer


17

ฉันมีวิธี DAO ที่ใช้ Spring สำหรับการเข้าถึง JDBC มันคำนวณอัตราความสำเร็จของผู้ขายในการขายสินค้า

นี่คือรหัส:

public BigDecimal getSellingSuccessRate(long seller_id) {
    String sql = "SELECT SUM(IF(sold_price IS NOT NULL, 1, 0))/SUM(1) 
                  FROM transaction WHERE seller_id = ?";
    Object[] args = {seller_id};
    return getJdbcTemplate().queryForObject(sql, args, BigDecimal.class);
}

ฉันควรจะทดสอบวิธีนี้หรือวิธี DAO กับ JUnit อย่างไร แนวทางปฏิบัติที่ดีที่สุดในการทดสอบตรรกะการเข้าถึงข้อมูลคืออะไร ฉันคิดว่าจะทดสอบกับฐานข้อมูลแบบฝังที่โหลดด้วยข้อมูลบางส่วน แต่เราไม่ควรทำการทดสอบการรวมที่คล้ายกับสภาพแวดล้อมการผลิตในแง่ของ RDBMS และสคีมาหรือไม่


ตรวจสอบDBUnit มันทำขึ้นเป็นพิเศษเพื่อแก้ปัญหาของคุณ
Sergio

คำตอบ:


15

ปัญหาในการใช้ฐานข้อมูล 'ของจริง' สำหรับการทดสอบหน่วยคือการตั้งค่าการถอดและการแยกการทดสอบ คุณไม่ต้องการหมุนฐานข้อมูล MySQL ใหม่ทั้งหมดและสร้างตารางและข้อมูลเพียงแค่การทดสอบหน่วยเดียว ปัญหานี้เกี่ยวข้องกับลักษณะภายนอกของฐานข้อมูลและฐานข้อมูลการทดสอบของคุณไม่ทำงานการทดสอบหน่วยของคุณล้มเหลว นอกจากนี้ยังมีปัญหาในการทำให้แน่ใจว่าคุณมีฐานข้อมูลเฉพาะสำหรับการทดสอบ พวกเขาสามารถเอาชนะได้ แต่มีคำตอบที่ง่ายกว่า

การเยาะเย้ยฐานข้อมูลเป็นหนึ่งตัวเลือกแต่จะไม่ทดสอบการสืบค้นจริงที่เรียกใช้ มันสามารถใช้เป็นวิธีที่ง่ายกว่ามากเมื่อคุณต้องการตรวจสอบให้แน่ใจว่าข้อมูลจาก DAO ผ่านระบบอย่างถูกต้อง แต่สำหรับการทดสอบ DAO เองคุณต้องการบางสิ่งบางอย่างที่อยู่เบื้องหลัง DAO มีข้อมูลและแบบสอบถามทำงานอย่างถูกต้อง

สิ่งแรกที่ต้องทำคือใช้ฐานข้อมูลในหน่วยความจำ HyperSQLเป็นตัวเลือกที่ยอดเยี่ยมสำหรับสิ่งนี้เพราะมันมีความสามารถในการเลียนแบบภาษาของฐานข้อมูลอื่น - เพื่อให้ความแตกต่างเล็กน้อยระหว่างฐานข้อมูลยังคงเหมือนเดิม (ชนิดข้อมูล, ฟังก์ชั่นและอื่น ๆ ) hsqldb ยังมีคุณสมบัติที่ดีสำหรับการทดสอบหน่วยอีกด้วย

db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;

สิ่งนี้จะโหลดสถานะของฐานข้อมูล (ตารางข้อมูลเริ่มต้น) จากtestDataไฟล์ shutdown=trueจะปิดฐานข้อมูลโดยอัตโนมัติเมื่อการเชื่อมต่อล่าสุดปิดลง

ใช้การฉีดแบบพึ่งพาให้หน่วยทดสอบเลือกฐานข้อมูลที่แตกต่างจากที่ใช้ในการสร้าง (หรือการทดสอบหรือท้องถิ่น)

DAO ของคุณจะใช้ฐานข้อมูลแบบฉีดซึ่งคุณสามารถเปิดการทดสอบกับฐานข้อมูล

การทดสอบหน่วยจะมีลักษณะเหมือน (พวงของสิ่งที่น่าเบื่อไม่รวมถึงความกะทัดรัด):

    @Before
    public void setUpDB() {
        DBConnection connection = new DBConnection();
        try {
            conn = connection.getDBConnection();
            insert = conn.prepareStatement("INSERT INTO data (txt, ts, active) VALUES (?, ?, ?)");
        } catch (SQLException e) {
            e.printStackTrace();
            fail("Error instantiating database table: " + e.getMessage());
        }
    }

    @After
    public void tearDown() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void addData(String txt, Timestamp ts, boolean active) throws Exception {
        insert.setString(1, txt);
        insert.setTimestamp(2, ts);
        insert.setBoolean(3, active);
        insert.execute();
    }

    @Test
    public void testGetData() throws Exception {
        // load data
        Calendar time = Calendar.getInstance();
        long now = time.getTimeInMillis();
        long then1h = now - (60 * 60 * 1000);  // one hour ago
        long then2m = now - (60 * 1000 * 2);   // two minutes ago
        addData("active_foo", new Timestamp(then1h), true);     // active but old
        addData("inactive_bar", new Timestamp(then1h), false);  // inactive and old
        addData("active_quz", new Timestamp(then2m), true);     // active and new
        addData("inactive_baz", new Timestamp(then2m), false);  // inactive and new

        DataAccess dao = new DataAccess();
        int count = 0;
        for (Data data : dao.getData()) {
            count++;
            assertTrue(data.getTxt().startsWith("active"));
        }

        assertEquals("got back " + count + " rows instead of 1", count, 1);
    }

ดังนั้นคุณจะได้รับการทดสอบหน่วยที่เรียก DAO และใช้ข้อมูลที่ตั้งค่าในฐานข้อมูลการบินที่มีอยู่ในช่วงระยะเวลาของการทดสอบ คุณไม่ต้องกังวลเกี่ยวกับทรัพยากรภายนอกหรือสถานะของฐานข้อมูลก่อนการรันหรือการกู้คืนสู่สถานะที่รู้จัก (เช่นกัน 'สถานะที่รู้จัก' คือ 'ไม่มีอยู่' ซึ่งเป็นสิ่งสำคัญที่จะเปลี่ยนกลับ)

DBUnitสามารถทำสิ่งที่ฉันอธิบายกระบวนการที่ง่ายกว่าในการตั้งค่าฐานข้อมูลการสร้างตารางและโหลดข้อมูล หากคุณต้องการใช้ฐานข้อมูลจริงด้วยเหตุผลบางอย่างนี่เป็นเครื่องมือที่ใช้งานได้ดีกว่า

รหัสข้างต้นเป็นส่วนหนึ่งของโครงการ Maven ที่ฉันเขียนเพื่อพิสูจน์แนวคิดTestWithHsqldb บน github


2
ฉันไม่รู้เกี่ยวกับชิ้นส่วนที่ HSQL สามารถล้อเลียนภาษาของผู้จำหน่ายรายอื่นได้ ขอขอบคุณ.
Michael

1
@Dog สามารถทำได้ผ่านคุณสมบัติฐานข้อมูลเช่นsql.syntax_mys=trueซึ่งเปลี่ยนวิธีการทำงานของ hsqldb: "คุณสมบัตินี้เมื่อตั้งค่าจริงเปิดใช้งานรองรับประเภท TEXT และ AUTO_INCREMENT และยังรองรับความสามารถในการใช้งานร่วมกับภาษาอื่น ๆ ในขณะที่sql.syntax_ora=true"คุณสมบัตินี้เมื่อตั้งค่าเป็นจริงรองรับการใช้งานที่ไม่ได้มาตรฐานนอกจากนี้ยังเปิดใช้งานไวยากรณ์ DUAL, ROWNUM, NEXTVAL และ CURRVAL และยังรองรับความสามารถในการใช้งานร่วมกับภาษาอื่น ๆ

DBUnit เป็นวิธีที่ :)
Silviu Burcea

@SilviuBurcea DBUnit สร้าง 'การประปา' ในการตั้งค่าสภาพแวดล้อมการทดสอบฐานข้อมูลที่ซับซ้อนได้ง่ายกว่าการทำด้วยมือ บางครั้งมันก็ยังมีประโยชน์ที่จะรู้วิธีการทำด้วยมือถ้าคุณต้องการ (วิธี 'ด้วยมือ' ดังกล่าวข้างต้นสามารถโยกย้ายไปยังภาษาอื่น ๆ ที่ DBUnit ไม่ใช่ตัวเลือก)

คุณสามารถดูAcolyte
cchantep

2

ก่อนอื่นคุณไม่ควรทำการทดสอบในสภาพแวดล้อมการผลิต คุณควรมีสภาพแวดล้อมการทดสอบที่สะท้อนสภาพแวดล้อมการผลิตของคุณและทำการทดสอบการรวมที่นั่น

ถ้าคุณทำอย่างนั้นคุณสามารถทำสิ่งต่าง ๆ

  • เขียนหน่วยทดสอบที่ทดสอบเพื่อดูว่า SQL ที่เหมาะสมถูกส่งไปยังรายการจำลองโดยใช้กรอบการเยาะเย้ยเช่น Mockito วิธีนี้จะทำให้มั่นใจได้ว่าวิธีการของคุณกำลังทำในสิ่งที่ควรทำและนำการรวมเข้ากับภาพ
  • ทดสอบการเขียนสคริปต์ SQL แสดงให้เห็นถึงความเหมาะสมของ SQL ที่คุณทดสอบในการทดสอบหน่วยของคุณ สิ่งนี้สามารถช่วยแก้ไขปัญหาการปรับแต่งใด ๆ ที่คุณอาจเกิดขึ้นเนื่องจากคุณสามารถเรียกใช้คำอธิบายและสิ่งต่างๆตามสคริปต์ทดสอบของคุณได้
  • ใช้ DBUnit ตามที่กล่าวถึงโดย @Sergio

เมื่อฉันพูดว่าสภาพแวดล้อมการผลิตจริง ๆ แล้วฉันหมายถึงการจำลองของมัน ขอบคุณสำหรับคำตอบของคุณฉันจะดู Mockito เพราะนั่นเป็นสิ่งที่ฉันต้องการเรียนรู้ด้วย
Michael

1

ในโครงการของเรานักพัฒนาแต่ละคนใช้ฐานข้อมูลเปล่าโครงสร้างของมันนั้นเหมือนกับฐานข้อมูลการผลิต

ในการทดสอบแต่ละหน่วย TestInitialize เราสร้างการเชื่อมต่อ & ธุรกรรมไปยังฐานข้อมูลรวมถึงวัตถุเริ่มต้นบางอย่างที่เราต้องการสำหรับการทดสอบแต่ละครั้ง และทุกอย่างจะถูกย้อนกลับไปหลังจากสิ้นสุดแต่ละวิธีหรือชั้นเรียน

ด้วยวิธีนี้มันเป็นไปได้ที่จะทดสอบชั้น sql ในความเป็นจริงทุกการสอบถามหรือเรียกฐานข้อมูลจะต้องมีการทดสอบด้วยวิธีนี้

ข้อเสียคือช้าดังนั้นเราจึงแยกโครงการออกจากการทดสอบตามปกติ เป็นไปได้ที่จะเร่งความเร็วโดยใช้ฐานข้อมูลในหน่วยความจำ แต่ความคิดยังคงเหมือนเดิม


หากใช้ฐานข้อมูลในหน่วยความจำแล้ววิธีการแบบดรอปดาวน์ก่อนที่ชุดทดสอบทั้งหมดจะสามารถใช้แทนธุรกรรมย้อนกลับได้ทันทีซึ่งเร็วกว่ามาก
Downhillski

ไม่เคยคิดที่จะทำแบบนั้นมาก่อน ในการทดสอบของเราการทดสอบส่วนใหญ่จะสร้าง 'x' ให้กับผู้ใช้ การสร้าง db หนึ่งครั้งจะหมายถึงการเปลี่ยนการทดสอบเพื่อนำวัตถุเหล่านั้นกลับมาใช้
Carra

ฉันรู้ว่าเราอยู่ในหน้าเดียวกันและฉันชอบวิธีการของคุณ วิธีการของคุณรับประกันว่าแต่ละกรณีทดสอบสามารถทำงานได้อย่างอิสระโดยไม่คำนึงถึงลำดับและทุกครั้งก่อนที่จะรันสถานะของตารางข้อมูลจะเหมือนกัน
Downhillski

ถูกต้องแล้วลำดับไม่สำคัญ เราเคยเห็นการทดสอบล้มเหลวมาก่อนเนื่องจากคำสั่งการทดสอบหน่วยนั้นแตกต่างกันในบิลด์พีซีและเครื่องท้องถิ่นของเรา
Carra
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.