namespaces ที่ไม่ระบุชื่อทำให้รหัสไม่สามารถทดสอบได้


12

นี่คือรหัส C ++ ทั่วไป:

foo.hpp

#pragma once

class Foo {
public:
  void f();
  void g();
  ...
};

foo.cpp

#include "foo.hpp"

namespace {
    const int kUpperX = 111;
    const int kAlternativeX = 222;

    bool match(int x) {
      return x < kUpperX || x == kAlternativeX;
    }
} // namespace

void Foo::f() {
  ...
  if (match(x)) return;
  ...

ดูเหมือนว่ารหัส C ++ ที่เหมาะสม - คลาส, ฟังก์ชันตัวช่วยmatchซึ่งใช้โดยวิธีการFoo, ค่าคงที่บางค่าสำหรับฟังก์ชันตัวช่วยนั้น

แล้วฉันต้องการเขียนแบบทดสอบ
มันจะมีเหตุผลอย่างสมบูรณ์แบบในการเขียนการทดสอบหน่วยแยกต่างหากmatchเพราะมันค่อนข้างไม่สำคัญ
แต่อยู่ในเนมสเปซที่ไม่ระบุชื่อ แน่นอนผมสามารถเขียนการทดสอบซึ่งจะเรียก
Foo::f()อย่างไรก็ตามมันจะไม่เป็นการทดสอบที่ดีถ้าFooหนักและซับซ้อนการทดสอบดังกล่าวจะไม่แยกผู้ทดสอบออกจากปัจจัยอื่น ๆ ที่ไม่เกี่ยวข้อง

ดังนั้นฉันต้องย้ายmatchทุกอย่างออกจากเนมสเปซนิรนาม

คำถาม:อะไรคือจุดที่ทำให้ฟังก์ชั่นและค่าคงที่อยู่ในเนมสเปซนิรนามถ้ามันทำให้มันใช้ไม่ได้ในการทดสอบ?


3
@ BЈовићอ่านรหัสอีกครั้ง - มีเนมสเปซนิรนามอยู่foo.cpp, ไม่ใช่ส่วนหัว! OP ดูเหมือนจะเข้าใจได้ค่อนข้างดีว่าคุณไม่ควรใส่เนมสเปซอันใดอันหนึ่งไว้ในส่วนหัว
amon

แนวคิดบางอย่างในการทดสอบหน่วยรหัส C ++ ในเนมสเปซที่ไม่มีชื่อ ... แต่ "จุด" คือ encapsulation นั้นดี ท้ายที่สุดแล้วมันเป็นปัญหาเดียวกันกับที่คุณมีกับฟังก์ชั่นสมาชิกส่วนตัว: พวกเขาไม่สามารถทดสอบโดยไม่ได้ตั้งใจ แต่คุณไม่ต้องการที่จะให้ข้อมูลซ่อนสำหรับการทดสอบหน่วย (เช่นstackoverflow.com/a/3676680/3235496 )
manlio

1
กรณีคุณมีแนวคิดไม่แตกต่างจากกรณีของการทดสอบ (หรือไม่ทดสอบ) วิธีส่วนตัว ดังนั้นเมื่อคุณค้นหา "การทดสอบหน่วยส่วนตัว" ที่นี่ในโปรแกรมเมอร์คุณจะได้รับคำตอบมากมายที่คุณสามารถนำไปใช้กับกรณีของคุณได้โดยตรง
Doc Brown

@DocBrown ฉันไม่ได้ถามวิธีการทดสอบฟังก์ชั่นในอานนท์ namespaces ฉันถาม "ทำไมใส่รหัสลง anon. namespaces?" (ดูข้อความท้ายคำถาม) ตำหนิ Ixrec สำหรับการเปลี่ยนชื่อเรื่องเป็นสิ่งที่แตกต่าง
Abyx

2
@Abyx: ในคำตอบอื่น ๆ ที่ฉันได้กล่าวไว้ข้างต้นคุณจะได้พบกับผู้เชี่ยวชาญจำนวนมากที่นี่ว่าเป็นความคิดที่ดีจริงๆที่จะทดสอบวิธีการส่วนตัวและfriendไม่แนะนำให้ใช้คำหลักที่มีวัตถุประสงค์นั้นในทางที่ผิด หากข้อ จำกัด สำหรับวิธีนำไปสู่สถานการณ์ที่คุณไม่สามารถทดสอบได้โดยตรงอีกต่อไปนั่นหมายความว่าวิธีการส่วนตัวไม่มีประโยชน์
Doc Brown

คำตอบ:


7

ถ้าคุณต้องการทดสอบรายละเอียดการติดตั้งแบบส่วนตัวคุณทำแบบเดียวกับการหลบเลี่ยงเนมสเปซที่ไม่มีชื่อเช่นเดียวกับสมาชิกคลาสส่วนตัว (หรือที่มีการป้องกัน):

บุกเข้าไปและปาร์ตี้

ในขณะที่ชั้นเรียนที่คุณทำผิดกฎfriendสำหรับเนมสเปซที่ไม่มีชื่อที่คุณละเมิด#include-mechanism ซึ่งไม่ได้บังคับให้คุณเปลี่ยนรหัส
ตอนนี้รหัสทดสอบของคุณ (หรือดีกว่าสิ่งเดียวที่จะเปิดเผยทุกอย่าง) อยู่ใน TU เดียวกันไม่มีปัญหา

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


6

ฟังก์ชั่นในตัวอย่างของคุณดูค่อนข้างซับซ้อนและอาจเป็นการดีกว่าที่จะย้ายไปยังส่วนหัวเพื่อวัตถุประสงค์ในการทดสอบหน่วย

จุดประสงค์ของการวางฟังก์ชั่นและค่าคงที่ลงในเนมสเปซที่ไม่ระบุชื่อคืออะไรหากมันทำให้ใช้งานไม่ได้ในการทดสอบ

เพื่อทำให้พวกเขาโดดเดี่ยวจากส่วนที่เหลือของโลก และไม่เพียง แต่ฟังก์ชั่นและค่าคงที่ที่คุณสามารถใส่ในเนมสเปซที่ไม่ระบุชื่อ - มันยังมีไว้สำหรับประเภท

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

ดังนั้นในเนมสเปซที่ไม่ระบุชื่อควรไปใช้ฟังก์ชั่นที่ง่ายมากบางครั้งค่าคงที่และประเภท (รวมถึง typedefs) ที่ใช้ในหน่วยการแปลนั้น


5

มันจะมีเหตุผลอย่างสมบูรณ์แบบในการเขียนการทดสอบหน่วยแยกต่างหากสำหรับการจับคู่เพราะมันค่อนข้างไม่สำคัญ

รหัสที่คุณแสดงmatchนั้นค่อนข้างน่าสนใจ 1 ซับโดยไม่มีกรณีขอบที่ยุ่งยากหรือว่าเป็นตัวอย่างที่ง่าย อย่างไรก็ตามฉันจะถือว่ามันง่ายขึ้น ...

คำถาม: อะไรคือจุดที่ทำให้ฟังก์ชั่นและค่าคงที่ลงในเนมสเปซนิรนามถ้ามันทำให้มันใช้ไม่ได้ในการทดสอบ?

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

เป้าหมายของการทดสอบหน่วยแม้แต่การทดสอบหน่วยงานเล็ก ๆ ก็ไม่เสมอไป คำถามเดียวกันนี้นำไปใช้กับฟังก์ชั่นขอบเขตไฟล์คงที่ใน C คุณสามารถทำให้คำถามยากขึ้นในการตอบโดยถามว่าทำไมนักพัฒนาใช้pimplsใน C ++ ซึ่งต้องใช้ทั้งสอง friendshipและ#includeเล่ห์เหลี่ยมไปที่กล่องสีขาว เช่น

จากมุมมองในทางปฏิบัติมันอาจฟังดูแย่ แต่matchอาจไม่ถูกนำไปใช้อย่างถูกต้องกับขอบเคสที่ทำให้เกิดการสะดุด อย่างไรก็ตามหากชั้นนอกเพียงชั้นเดียวFooที่มีการเข้าถึงmatchไม่สามารถใช้งานในลักษณะที่พบกรณีขอบเหล่านั้นก็ค่อนข้างไม่เกี่ยวข้องกับความถูกต้องของFooที่matchมีกรณีขอบเหล่านี้ที่จะไม่พบเว้นแต่Fooการเปลี่ยนแปลง ณ จุดนี้ การทดสอบFooจะล้มเหลวและเราจะทราบทันที

ความคิดครอบงำมากขึ้นกระตือรือร้นที่จะทดสอบทุกรายละเอียดการดำเนินงานภายใน (อาจจะเป็นซอฟต์แวร์ที่เป็นภารกิจที่สำคัญเช่น) อาจต้องการที่จะทำลายและปาร์ตี้ แต่ผู้คนจำนวนมากไม่จำเป็นต้องคิดว่าเป็นความคิดที่ดีที่สุดเพราะมันจะสร้าง การทดสอบที่เปราะบางที่สุดเท่าที่จะจินตนาการได้ YMMV แต่ฉันแค่อยากจะตอบคำถามของคำถามนี้ซึ่งทำให้ดูเหมือนว่าการทดสอบระดับรายละเอียดในระดับ uber-fine-grained-internal- รายละเอียดควรเป็นเป้าหมายสุดท้ายเมื่อแม้แต่ความคิดการทดสอบหน่วยที่เข้มงวดที่สุดอาจผ่อนคลายเล็กน้อยที่นี่ และหลีกเลี่ยงการเอ็กซเรย์ตรวจภายในของทุกชั้นเรียน

แล้วทำไมคนถึงกำหนดฟังก์ชั่นในเนมสเปซที่ไม่ระบุชื่อใน C ++ หรือฟังก์ชั่นสแตติกขอบเขตไฟล์ที่มีการเชื่อมโยงภายในใน C ซึ่งซ่อนจากโลกภายนอก และที่สำคัญก็คือ: ซ่อนพวกเขาจากโลกภายนอก ที่มีจำนวนของผลกระทบจากการลดเวลาการรวบรวมเพื่อลดความซับซ้อน (สิ่งที่ไม่สามารถเข้าถึงได้ที่อื่นไม่สามารถทำให้เกิดปัญหาที่อื่น) และอื่น ๆ ความน่าจะเป็นที่พิสูจน์ได้ของรายละเอียดการปฏิบัติงานส่วนตัว / ภายในไม่ใช่สิ่งอันดับหนึ่งในใจของผู้คนเมื่อพวกเขาทำพูดลดเวลาสร้างและซ่อนความซับซ้อนที่ไม่จำเป็นจากโลกภายนอก


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