2012-02-09 10 views
14

Tôi đang cố gắng để viết lớp thử nghiệm cho các phương pháp sau đâymùa xuân trong Mockito

public class CustomServiceImpl implements CustomService { 
    @Value("#{myProp['custom.url']}") 
    private String url; 
    @Autowire 
    private DataService dataService; 

Tôi đang sử dụng giá trị url tiêm tại một trong những phương thức trong lớp. Để kiểm tra điều này tôi đã viết một lớp junit

@RunWith(MockitoJUnitRunner.class) 
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" }) 
public CustomServiceTest{ 
    private CustomService customService; 
    @Mock 
    private DataService dataService; 
    @Before 
    public void setup() { 
     customService = new CustomServiceImpl(); 
     Setter.set(customService, "dataService", dataService); 
    }  
    ... 
} 

public class Setter { 
    public static void set(Object obj, String fieldName, Object value) throws Exception { 
     Field field = obj.getClass().getDeclaredField(fieldName); 
     field.setAccessible(true); 
     field.set(obj, value); 
    } 
} 

Trong applicationContext-test.xml Tôi đang tải các tập tin bất động sản sử dụng

<util:properties id="myProp" location="myProp.properties"/> 

Nhưng giá trị url là không nhận được nạp trong CustomService khi chạy thử nghiệm. Tôi đã tự hỏi nếu có anyway để có được điều này thực hiện.

Cảm ơn

+4

Nếu bạn đang chế nhạo 'CustomService', thì cách sử dụng' CustomServiceImpl' như thế nào? Điều đó không có ý nghĩa. – skaffman

+0

Tôi đang sử dụng CustomeServiceImpl. Đã cập nhật mã. Làm thế nào tôi có thể giả định giá trị của url mà cần phải được đọc từ các tập tin thuộc tính? – rohit

+1

Vì nó bây giờ là viết tắt, bạn không sử dụng Spring để đặt giá trị customService, bạn đặt giá trị theo cách thủ công trong phương thức setup() với mã này: 'customService = new CustomServiceImpl();' – DwB

Trả lời

3

Bạn có thể tự động chuyển thành trình tắt (setter), thay vì chỉ chú thích trường riêng tư. Sau đó, bạn có thể sử dụng setter đó từ lớp thử nghiệm của bạn. Không cần phải công khai nó, gói riêng tư sẽ làm như Spring vẫn có thể truy cập nó, nhưng nếu không chỉ thử nghiệm của bạn mới có thể vào được (hoặc mã khác trong cùng một gói).

@Value("#{myProp['custom.url']}") 
String setUrl(final String url) { 
    this.url = url; 
} 

Tôi không phải là người hâm mộ tự động khác (so với mã số của tôi) chỉ để thử nghiệm, nhưng thay thế thay đổi lớp đang kiểm tra, từ bài kiểm tra, đơn giản là không biết.

+0

bạn có biết điều này cũng hoạt động với chú thích '@ Resource' không? – OscarRyz

1

Bạn không nên giả mạo điều bạn đang cố thử nghiệm. Đó là vô nghĩa vì bạn sẽ không chạm vào bất kỳ mã nào mà bạn đang thử nghiệm. Thay vào đó hãy lấy ví dụ của CustomerServiceImpl từ ngữ cảnh.

+0

Đừng cam kết tội lỗi hồng y của Thử nghiệm Đơn vị. –

12

Tôi đồng ý với nhận xét của @skaffman.

Bên cạnh thử nghiệm của bạn sử dụng MockitoJUnitRunner, do đó nó sẽ không tìm kiếm bất kỳ công cụ mùa xuân nào, mục đích duy nhất này là khởi tạo Mockito mocks. Các ContextConfiguration là không đủ để dây những thứ với mùa xuân. Về mặt kỹ thuật với JUnit bạn có thể sử dụng nhân vật sau nếu bạn muốn các công cụ liên quan đến mùa xuân: SpringJUnit4ClassRunner.

Cũng như bạn đang viết Kiểm tra đơn vị bạn có thể muốn xem xét lại việc sử dụng lò xo. Sử dụng dây lò xo trong một thử nghiệm đơn vị là sai. Tuy nhiên, nếu bạn thay vì viết một bài kiểm tra tích hợp thì tại sao bạn lại sử dụng Mockito ở đó, điều đó không có ý nghĩa (như được nói bởi nghệ sĩ skaffman)!

EDIT: Bây giờ trong mã của bạn, bạn trực tiếp đặt CustomerServiceImpl trong khối trước của mình, điều đó cũng không có ý nghĩa. Mùa xuân không liên quan gì cả!

@Before 
public void setup() { 
    customService = new CustomServiceImpl(); 
    Setter.set(customService, "dataService", dataService); 
} 

EDIT 2: Nếu bạn muốn viết một Unit Test của CustomerServiceImpl, sau đó tránh nội dung mùa xuân và tiêm trực tiếp giá trị của tài sản. Ngoài ra, bạn có thể sử dụng Mockito để tiêm DataService mô hình thắt lưng vào ví dụ được kiểm tra.

@RunWith(MockitoJUnitRunner.class) 
public CustomServiceImplTest{ 
    @InjectMocks private CustomServiceImpl customService; 
    @Mock private DataService dataService; 

    @Before void inject_url() { customerServiceImpl.url = "http://..."; } 

    @Test public void customerService_should_delegate_to_dataService() { ... } 
} 

Như bạn có thể nhận thấy tôi đang sử dụng truy cập trực tiếp vào trường url, trường có thể là gói hiển thị. Đây là giải pháp thử nghiệm để thực sự chèn giá trị URL khi Mockito chỉ tiêm mocks.

+0

Bằng cách "xem xét lại việc sử dụng mùa xuân", bạn muốn xem xét lại việc sử dụng các tệp cấu hình Spring để thực hiện việc tiêm phụ thuộc cho _this test_, phải không? Bạn không gợi ý rằng anh ta từ bỏ việc sử dụng Spring trong ứng dụng của mình. – jhericks

+4

@jhericks Không, tôi muốn xóa mã liên quan đến Spring trong Bài kiểm tra Đơn vị. Tôi thường có một khó khăn về điều này: Tôi không khuyến khích sử dụng mã keo như Sprin trong bất kỳ ** Unit Test ** nào. Mục đích của ** Unit Test ** là kiểm tra cách ly mã sản xuất (ở đây 'CustomerServiceImpl'). Điều này thực sự có vẻ là ý định của tác giả của vấn đề. Nếu mùa xuân là bắt buộc vì một lý do nào đó, thì nó bắt đầu là ** Kiểm tra tích hợp ** mà không có ý nghĩa tương tự trong bài kiểm tra. – Brice

+0

EDIT 2 đã khắc phục sự cố của tôi, cảm ơn bạn! –

31
import org.springframework.test.util.ReflectionTestUtils; 

@RunWith(MockitoJUnitRunner.class) 
public CustomServiceTest{ 

@InjectMock 
private CustomServiceImpl customService; 

@Mock 
private DataService dataService; 

@Before 
public void setup() { 
    ReflectionTestUtils.setField(customService, "url", "http://someurl"); 
}  
... 
} 
+0

Bạn cũng có thể giải thích giải pháp của mình bằng một vài từ không? – Magnilex

+0

Câu hỏi từ Rohit có thể có vấn đề khác, nhưng trong khi tìm kiếm giải pháp cho vấn đề của tôi, giải pháp này chính xác là điều tôi muốn, cảm ơn Robert – mikeapr4

+0

Đơn giản và làm việc, tôi thích nó !! –

1

Tôi có danh sách chuỗi đọc từ tệp thuộc tính. Phương thức ReflectionTestUtils class setField được sử dụng trong @Before block đã giúp tôi thiết lập các giá trị này trước khi thực thi thử nghiệm của tôi. Nó làm việc hoàn hảo ngay cả đối với lớp dao của tôi mà là tùy thuộc vào lớp DaoSupport chung.

@Before 
public void setList() { 
    List<String> mockedList = new ArrayList<>(); 
    mockedSimList.add("CMS"); 
    mockedSimList.add("SDP"); 
    ReflectionTestUtils.setField(mockedController, "ActualListInController", 
      mockedList); 
}