2009-05-06 12 views
6

Cảm ơn tất cả vì sự giúp đỡ của bạn. Một số câu trả lời bạn đăng tải (như tôi lẽ ra phải đợi) cho thấy toàn bộ cách tiếp cận của tôi là sai, hoặc mã cấp thấp đó không bao giờ phải biết nó có đang chạy trong vùng chứa hay không. Tôi định đông y. Tuy nhiên, tôi đang đối phó với một ứng dụng kế thừa phức tạp và không có tùy chọn thực hiện tái cấu trúc chính cho vấn đề hiện tại.Các bài kiểm tra đơn vị nên thiết lập nguồn dữ liệu khi không chạy trong máy chủ ứng dụng như thế nào?

Hãy để tôi quay lại và đặt câu hỏi là câu hỏi ban đầu có động cơ của tôi.

Tôi có một ứng dụng cũ chạy dưới JBoss và đã thực hiện một số sửa đổi đối với mã cấp thấp hơn. Tôi đã tạo ra một bài kiểm tra đơn vị để sửa đổi của tôi. Để chạy thử nghiệm, tôi cần kết nối với cơ sở dữ liệu.

Mã di sản được nguồn dữ liệu theo cách này:

(jndiName là một chuỗi định nghĩa)

Context ctx = new InitialContext(); 
DataSource dataSource = (DataSource) ctx.lookup(jndiName); 

Vấn đề của tôi là khi tôi chạy mã này dưới sự kiểm tra đơn vị, bối cảnh không có dữ liệu nguồn được xác định. Giải pháp của tôi cho điều này là cố gắng xem liệu tôi có đang chạy dưới máy chủ ứng dụng hay không và nếu không, hãy tạo DataSource thử nghiệm và trả về nó. Nếu tôi đang chạy dưới máy chủ ứng dụng thì tôi sử dụng mã ở trên.

Vì vậy, câu hỏi thực sự của chúng tôi là: Cách chính xác để thực hiện việc này là gì? Có cách nào được phê duyệt kiểm tra đơn vị có thể thiết lập ngữ cảnh để trả về nguồn dữ liệu thích hợp để mã đang được kiểm tra không cần phải biết nơi nó đang chạy không?


Đối Bối cảnh: MY ORIGINAL HỎI:

Tôi có một số mã Java mà cần phải biết hay không nó đang chạy dưới JBoss. Có một cách kinh điển cho mã để cho biết liệu nó đang chạy trong một container?

Cách tiếp cận đầu tiên của tôi được phát triển thông qua thử nghiệm và bao gồm nhận bối cảnh ban đầu và thử nghiệm để có thể tra cứu các giá trị nhất định.

private boolean isRunningUnderJBoss(Context ctx) { 
     boolean runningUnderJBoss = false; 
     try { 
      // The following invokes a naming exception when not running under 
      // JBoss. 
      ctx.getNameInNamespace(); 

      // The URL packages must contain the string "jboss". 
      String urlPackages = (String) ctx.lookup("java.naming.factory.url.pkgs"); 
      if ((urlPackages != null) && (urlPackages.toUpperCase().contains("JBOSS"))) { 
       runningUnderJBoss = true; 
      } 
     } catch (Exception e) { 
      // If we get there, we are not under JBoss 
      runningUnderJBoss = false; 
     } 
     return runningUnderJBoss; 
    } 

Context ctx = new InitialContext(); 
if (isRunningUnderJboss(ctx) 
{ 
......... 

Hiện tại, điều này có vẻ hiệu quả nhưng cảm giác như bị hack. Cách chính xác để làm điều này là gì? Lý tưởng nhất, tôi muốn một cách mà sẽ làm việc với một loạt các máy chủ ứng dụng, không chỉ JBoss.

+0

Tôi cho rằng, thay vì JBoss, bạn có nghĩa là Tomcat? (Được nhúng trong JBoss.) – Eddie

+0

Bạn đang cố xác định vùng chứa so với không chứa hoặc loại vùng chứa ứng dụng đang chạy? – Kapsh

+1

Bạn có thể mở rộng về lý do tại sao bạn cần biết liệu nó có đang chạy trong vùng chứa không? Điều đó có thể giúp trực tiếp câu trả lời. –

Trả lời

2

Toàn bộ cách tiếp cận cảm thấy sai lầm với tôi. Nếu ứng dụng của bạn cần biết vùng chứa nào đang chạy trong khi bạn đang làm gì đó sai.

Khi tôi sử dụng Spring tôi có thể di chuyển từ Tomcat sang WebLogic và ngược lại mà không thay đổi bất cứ điều gì. Tôi chắc chắn rằng với cấu hình thích hợp, tôi có thể làm cùng một thủ thuật với JBOSS. Đó là mục tiêu tôi muốn quay.

+0

Nhiều người đã đưa ra đề xuất hữu ích, nhưng câu trả lời này mô tả cách tiếp cận mà tôi đã kết thúc. Tôi đã loại bỏ bất kỳ lôgic nào trong mã đang được thử nghiệm đang cố gắng xem nó có trong môi trường thử nghiệm hay không. Sau đó tôi đào thông qua tài liệu và tìm ra cách tạo ngữ cảnh và nguồn dữ liệu ban đầu của riêng mình để mã dưới thử nghiệm chỉ có nguồn dữ liệu khác khi chạy dưới JUnit. Cảm ơn tất cả những gì đã trả lời. –

+1

Đánh dấu - bạn có nhớ gửi cấu hình đó không? Nó sẽ hữu ích cho những người khác thấy mình trong một tình huống tương tự. –

1

Có lẽ một cái gì đó như thế này (xấu xí nhưng nó có thể làm việc)

private void isRunningOn(String thatServerName) { 

    String uniqueClassName = getSpecialClassNameFor(thatServerName); 
    try { 
     Class.forName(uniqueClassName); 
    } catch (ClassNotFoudException cnfe) { 
     return false; 
    } 
    return true; 
} 

Các getSpecialClassNameFor phương pháp sẽ trở lại một lớp đó là duy nhất cho mỗi máy chủ ứng dụng (và có thể trở lại tên lớp mới khi nhiều ứng dụng máy chủ được gia tăng)

Sau đó, bạn sử dụng nó như:

if(isRunningOn("JBoss")) { 
     createJBossStrategy....etcetc 
    } 
-1

Một cách sạch để làm w này có thể có các trình lắng nghe vòng đời được cấu hình trong web.xml. Đây có thể thiết lập cờ toàn cầu nếu bạn muốn. Ví dụ: bạn có thể xác định ServletContextListener trong web.xml và phương thức contextInitialized của mình, đặt cờ toàn cầu mà bạn đang chạy bên trong vùng chứa. Nếu cờ toàn cầu không được đặt, thì bạn không chạy bên trong vùng chứa.

+0

Vì khi nào cờ toàn cầu sạch sẽ? –

+2

Cờ toàn cầu không phải lúc nào cũng xấu xa. Quá nhiều trạng thái toàn cầu thường chỉ ra một thiết kế xấu, nhưng một lá cờ toàn cầu duy nhất là hợp lý cho loại thông tin này. Nếu bạn có một ý tưởng tốt hơn, hãy tự mình đề xuất. – Eddie

1

Có một số cách để giải quyết vấn đề này. Một là truyền đối tượng Context tới lớp khi nó được kiểm tra đơn vị. Nếu bạn không thể thay đổi chữ ký của phương thức, hãy cấu trúc lại việc tạo ngữ cảnh inital thành một phương thức được bảo vệ và kiểm tra một lớp con trả về đối tượng bối cảnh giả định bằng cách ghi đè phương thức. Điều đó ít nhất có thể đặt lớp theo thử nghiệm để bạn có thể cấu trúc lại các lựa chọn thay thế tốt hơn từ đó.

Tùy chọn tiếp theo là tạo kết nối cơ sở dữ liệu cho một nhà máy có thể biết nó có nằm trong thùng chứa hay không và thực hiện điều thích hợp trong từng trường hợp.

Một điều cần suy nghĩ là - khi bạn có kết nối cơ sở dữ liệu này ra khỏi vùng chứa, bạn sẽ làm gì với nó? Nó là dễ dàng hơn, nhưng nó không phải là khá một bài kiểm tra đơn vị nếu bạn phải mang theo toàn bộ lớp truy cập dữ liệu.

Để được trợ giúp thêm về hướng di chuyển mã kế thừa này theo thử nghiệm đơn vị, tôi đề nghị bạn xem số Working Effectively with Legacy Code của Michael Feather.

5

Toàn bộ khái niệm quay lại phía trước. Mã cấp thấp hơn không nên thực hiện loại thử nghiệm này. Nếu bạn cần một triển khai khác, hãy chuyển nó xuống tại một điểm thích hợp.

+0

Có lẽ anh ấy đang cố gắng xác định xem anh ấy có cần triển khai khác ở nơi đầu tiên không. – OscarRyz

+0

Sau đó, anh ta cần phải đặt cao hơn. –

4

Một số kết hợp của Dependency Injection (cho dù thông qua Spring, tệp cấu hình hoặc đối số chương trình) và Pattern Factory thường hoạt động tốt nhất.

Ví dụ, tôi chuyển đối số cho tập lệnh Ant của tôi thiết lập tệp cấu hình tùy thuộc vào tai hoặc chiến tranh đang phát triển, thử nghiệm hoặc môi trường sản xuất.

+0

+1 cho DI, cách tiếp cận này sẽ cho phép bạn dễ dàng vượt qua các đối tượng giả trong –

+0

Đồng ý. Nếu bạn muốn giữ nó đơn giản, bạn chỉ có thể sử dụng một Mẫu Chiến lược để lấy nguồn dữ liệu khi chạy. Tệp cấu hình sẽ xác định chiến lược nào sẽ sử dụng dựa trên môi trường, giống như tùy chọn được đề xuất trong câu trả lời. Bạn sẽ cần một nguồn dữ liệu dev của khóa học, nhưng đó là khá đơn giản để viết. – Robin

1
Context ctx = new InitialContext(); 
DataSource dataSource = (DataSource) ctx.lookup(jndiName); 

Ai xây dựng các InitialContext? Việc xây dựng của nó phải nằm ngoài mã mà bạn đang cố gắng kiểm tra hoặc nếu không bạn sẽ không thể giả lập ngữ cảnh.

Vì bạn đã nói rằng bạn đang làm việc trên một ứng dụng kế thừa, trước tiên hãy cấu trúc lại mã để bạn có thể dễ dàng phụ thuộc khi chèn ngữ cảnh hoặc nguồn dữ liệu vào lớp. Sau đó, bạn có thể dễ dàng viết các bài kiểm tra cho lớp đó.

Bạn có thể chuyển mã cũ bằng cách có hai hàm tạo, như trong mã bên dưới, cho đến khi bạn đã cấu trúc lại mã để tạo lớp. Bằng cách này bạn có thể dễ dàng kiểm tra Foo và bạn có thể giữ mã sử dụng Foo không thay đổi. Sau đó, bạn có thể từ từ refactor mã, do đó, các nhà xây dựng cũ là hoàn toàn loại bỏ và tất cả các phụ thuộc được tiêm phụ thuộc.

public class Foo { 
    private final DataSource dataSource; 
    public Foo() { // production code calls this - no changes needed to callers 
    Context ctx = new InitialContext(); 
    this.dataSource = (DataSource) ctx.lookup(jndiName); 
    } 
    public Foo(DataSource dataSource) { // test code calls this 
    this.dataSource = dataSource; 
    } 
    // methods that use dataSource 
} 

Nhưng trước khi bắt đầu thực hiện việc tái cấu trúc, bạn nên có một số thử nghiệm tích hợp để che lưng. Nếu không, bạn không thể biết ngay cả các phép tái cấu trúc đơn giản, chẳng hạn như di chuyển tra cứu DataSource tới hàm tạo, phá vỡ một cái gì đó. Sau đó, khi mã được tốt hơn, dễ kiểm thử hơn, bạn có thể viết các bài kiểm tra đơn vị. (Theo định nghĩa, nếu một thử nghiệm chạm vào hệ thống tập tin, mạng hoặc cơ sở dữ liệu, nó không phải là một thử nghiệm đơn vị - nó là một thử nghiệm tích hợp.)

Lợi ích của các bài kiểm tra đơn vị là chúng chạy nhanh - hàng trăm hoặc hàng nghìn mỗi giây - và rất tập trung để chỉ thử nghiệm một hành vi tại một thời điểm. Điều đó làm cho nó có thể chạy sau đó thường xuyên (nếu bạn ngần ngại chạy tất cả các bài kiểm tra đơn vị sau khi thay đổi một dòng, chúng chạy quá chậm) để bạn nhận được phản hồi nhanh chóng. Và bởi vì chúng rất tập trung, bạn sẽ biết chỉ bằng cách nhìn vào tên của bài kiểm tra thất bại mà chính xác nơi mà trong mã sản xuất lỗi đó là.

Lợi ích của các thử nghiệm tích hợp là chúng đảm bảo rằng tất cả các bộ phận được cắm vào nhau một cách chính xác. Điều đó cũng rất quan trọng, nhưng bạn không thể chạy chúng thường xuyên vì những thứ như chạm vào cơ sở dữ liệu khiến chúng rất chậm. Nhưng bạn vẫn nên chạy chúng ít nhất một lần một ngày trên máy chủ tích hợp liên tục của bạn.

Các vấn đề liên quan