Thử nghiệm đơn vị thời gian thực - hoặc cách làm thế nào để chế giễu ngay bây giờ


9

Khi bạn đang làm việc trên một tính năng phụ thuộc vào thời gian ... Làm thế nào để bạn tổ chức thử nghiệm đơn vị? Khi đơn vị kiểm tra các kịch bản phụ thuộc vào cách chương trình của bạn diễn giải "ngay bây giờ", làm thế nào để bạn thiết lập chúng?

Chỉnh sửa thứ hai: Sau một vài ngày đọc kinh nghiệm của bạn

Tôi có thể thấy rằng các kỹ thuật để đối phó với tình huống này thường xoay quanh một trong ba nguyên tắc sau:

  • Thêm (mã cứng) một phụ thuộc: thêm một lớp nhỏ qua hàm / đối tượng thời gian và luôn gọi hàm datetime của bạn thông qua lớp này. Bằng cách này, bạn có thể kiểm soát thời gian trong các trường hợp thử nghiệm.
  • Sử dụng giả: mã của bạn giữ nguyên chính xác. Trong các thử nghiệm của bạn, bạn thay thế đối tượng thời gian bằng một đối tượng thời gian giả. Đôi khi, giải pháp liên quan đến việc sửa đổi đối tượng thời gian thực được cung cấp bởi ngôn ngữ lập trình của bạn.
  • Sử dụng nội xạ phụ thuộc: Xây dựng mã của bạn để bất kỳ tham chiếu thời gian nào được truyền dưới dạng tham số. Sau đó, bạn có quyền kiểm soát các tham số trong các bài kiểm tra.

Các kỹ thuật (hoặc thư viện) dành riêng cho một ngôn ngữ rất được hoan nghênh và sẽ được nâng cao nếu một đoạn mã minh họa xuất hiện. Sau đó, thú vị nhất là làm thế nào các nguyên tắc tương tự có thể được áp dụng trên bất kỳ nền tảng nào. Và vâng ... Nếu tôi có thể áp dụng nó ngay lập tức trong PHP, tốt hơn là tốt hơn;)

Hãy bắt đầu với một ví dụ đơn giản: một ứng dụng đặt phòng cơ bản.

Giả sử chúng ta có API JSON và hai thông báo: thông báo yêu cầu và thông báo xác nhận. Một kịch bản tiêu chuẩn diễn ra như sau:

  1. Bạn đưa ra yêu cầu. Bạn nhận được phản hồi với mã thông báo. Tài nguyên cần thiết để thực hiện yêu cầu đó bị hệ thống chặn trong 5 phút.
  2. Bạn xác nhận một yêu cầu, được xác định bởi mã thông báo. Nếu mã thông báo được phát hành trong vòng 5 phút, nó sẽ được chấp nhận (tài nguyên vẫn có sẵn). Nếu hơn 5 phút trôi qua, bạn phải đưa ra yêu cầu mới (tài nguyên đã được giải phóng. Bạn cần kiểm tra lại tính khả dụng của nó một lần nữa).

Ở đây có kịch bản thử nghiệm tương ứng:

  1. Tôi đưa ra yêu cầu. Tôi xác nhận (ngay lập tức) với mã thông báo tôi nhận được. Xác nhận của tôi được chấp nhận.

  2. Tôi đưa ra yêu cầu. Tôi đợi 3 phút. Tôi xác nhận với mã thông báo tôi nhận được. Xác nhận của tôi được chấp nhận.

  3. Tôi đưa ra yêu cầu. Tôi đợi 6 phút. Tôi xác nhận với mã thông báo tôi nhận được. Xác nhận của tôi bị từ chối.

Làm thế nào chúng ta có thể lập trình các bài kiểm tra đơn vị này? Kiến trúc nào chúng ta nên sử dụng để các chức năng này vẫn có thể kiểm tra được?

Chỉnh sửa Lưu ý: Khi chúng tôi thực hiện một yêu cầu, thời gian được lưu trữ trong cơ sở dữ liệu ở định dạng mất bất kỳ thông tin nào về mili giây.

EXTRA - nhưng có lẽ hơi dài dòng: Ở đây có chi tiết về những gì tôi phát hiện ra khi làm "bài tập về nhà" của mình:

  1. Tôi đã xây dựng tính năng của mình với sự phụ thuộc vào chức năng thời gian của riêng tôi. Hàm VirtualDateTime của tôi có một phương thức tĩnh get_time () mà tôi gọi nơi tôi đã sử dụng để gọi DateTime () mới. Chức năng thời gian này cho phép tôi mô phỏng và kiểm soát thời gian là "bây giờ", để tôi có thể xây dựng các bài kiểm tra như: "Đặt ngay bây giờ đến ngày 21 tháng 1 năm 2014 16h15. Đưa ra yêu cầu. Di chuyển trước 3 phút. Xác nhận." Điều này hoạt động tốt, với chi phí của sự phụ thuộc và "mã không đẹp lắm".

  2. Một giải pháp tích hợp hơn một chút sẽ là xây dựng hàm myDateTime của riêng tôi để mở rộng DateTime với các chức năng "thời gian ảo" bổ sung (quan trọng nhất là đặt ngay bây giờ thành những gì tôi muốn). Điều này làm cho mã của giải pháp đầu tiên thanh lịch hơn một chút (sử dụng myDateTime mới thay vì DateTime mới), nhưng kết thúc rất giống nhau: Tôi phải xây dựng tính năng của mình bằng lớp của riêng mình, do đó tạo ra sự phụ thuộc.

  3. Tôi có mặc dù về việc hack chức năng DateTime, để làm cho nó hoạt động với sự phụ thuộc của tôi khi tôi cần. Có cách nào đơn giản và gọn gàng để thay thế một lớp bằng một lớp khác không? (Tôi nghĩ rằng tôi đã có câu trả lời cho điều đó: Xem không gian tên bên dưới).

  4. Trong môi trường PHP, tôi đọc "Runkit" có thể cho phép tôi thực hiện việc này (hack chức năng DateTime) một cách linh hoạt. Điều tuyệt vời là tôi sẽ có thể kiểm tra tôi đang chạy trong môi trường thử nghiệm trước khi sửa đổi bất cứ điều gì về DateTime và để nó không bị ảnh hưởng trong sản xuất [1] . Điều này nghe có vẻ an toàn và sạch sẽ hơn bất kỳ hack DateTime thủ công nào.

  5. Sự phụ thuộc của hàm đồng hồ trong mỗi lớp sử dụng thời gian [2] . Đây không phải là quá mức? Tôi thấy rằng trong một số môi trường, nó rất hữu ích [5] . Trong trường hợp này, tôi không thích nó lắm.

  6. Loại bỏ sự phụ thuộc thời gian trong tất cả các chức năng [2] . Điều này luôn luôn khả thi? (Xem thêm ví dụ dưới đây)

  7. Sử dụng không gian tên [2] [3] ? Điều này có vẻ khá tốt. Tôi có thể giả định DateTime bằng chức năng DateTime của riêng mình kéo dài \ DateTime ... Sử dụng DateTime :: setNow ("2014-01-21 16:15:00") và DateTime :: chờ đợi ("+ 3 phút"). Điều này bao gồm khá nhiều những gì tôi cần. Nếu chúng ta sử dụng hàm time () thì sao? Hoặc các chức năng thời gian khác? Tôi vẫn phải tránh sử dụng chúng trong mã gốc của tôi. Hoặc tôi sẽ cần đảm bảo bất kỳ hàm thời gian PHP nào tôi sử dụng trong mã của mình bị ghi đè trong các thử nghiệm của mình ... Có thư viện nào có sẵn để làm việc này không?

  8. Tôi đã tìm kiếm một cách để thay đổi thời gian hệ thống chỉ cho một chủ đề. Dường như không có [4] . Đây là một điều đáng tiếc: một hàm PHP "đơn giản" thành "Đặt thời gian thành ngày 21 tháng 1 năm 2014 16h15 cho chủ đề này" sẽ là một tính năng tuyệt vời cho loại thử nghiệm này.

  9. Thay đổi ngày và giờ hệ thống để kiểm tra, sử dụng exec (). Điều này có thể giải quyết nếu bạn không sợ làm hỏng những thứ khác trên máy chủ. Và bạn cần thiết lập lại thời gian hệ thống sau khi bạn chạy thử nghiệm. Nó có thể thực hiện các mẹo trong một số tình huống, nhưng cảm thấy khá "hacky".

Điều này có vẻ như là một vấn đề rất tiêu chuẩn với tôi. Tuy nhiên, tôi vẫn bỏ lỡ một cách đơn giản để đối phó với nó. Có lẽ tôi đã bỏ lỡ điều gì? Hãy chia sẻ kinh nghiệm của bạn!

LƯU Ý: Dưới đây là một số tình huống khác mà chúng tôi có thể có nhu cầu thử nghiệm tương tự.

  • Bất kỳ tính năng nào hoạt động với một số loại thời gian chờ (ví dụ: trò chơi cờ vua?)

  • xử lý hàng đợi kích hoạt các sự kiện tại một thời điểm nhất định (trong kịch bản trên, chúng ta có thể tiếp tục như vậy: một ngày trước khi đặt chỗ bắt đầu, tôi muốn gửi thư cho người dùng với tất cả các chi tiết. Kịch bản thử nghiệm ...)

  • Bạn muốn thiết lập một môi trường thử nghiệm với dữ liệu được thu thập trong quá khứ - bạn muốn thấy nó giống như bây giờ. [1]

1 /programming/3271735/simulation-different-server-datetimes-in-php

2 /programming/4221480/how-to-change-civerse-time-for-unit-testing-date-fifts-in-php

3 http://www.schmengler-se.de/en/2011/03/php-mocking-built-in-fifts-like-time-in-unit-tests/

4 /programming/3923848/change-todays-date-and-time-in-php

5 Đơn vị kiểm tra mã giới hạn thời gian


1
Trong python (và tôi tưởng tượng các ngôn ngữ động khác có những thứ tương tự) có thư viện tuyệt vời này: github.com/spulec/freezegun
Ismail Badawi

2
Một khi bạn nhận ra bây giờ chỉ là một thời điểm điều này trở nên dễ dàng hơn nhiều.
Wyatt Barnett

Trong nhiều trường hợp, việc chuyển một giá trị DateTime nowvào mã thay vì đồng hồ sẽ đơn giản hơn .
CodeInChaos

@IsmailBadawi - điều này thực sự trông khá tuyệt. Vì vậy, theo cách này bạn không phải thay đổi cách viết mã theo bất kỳ cách nào - bạn chỉ cần thiết lập các bài kiểm tra của mình bằng thư viện, đúng không? Làm thế nào nó sẽ trông giống như với ví dụ đặt phòng của chúng tôi?
mika

@WyattBarnett Chắc chắn. Thời điểm tôi mất kiểm soát thời điểm này là khi tôi sử dụng DateTime () mới mà không có tham số. Bạn có nghĩ rằng đó là thói quen xấu khi sử dụng nó trong một lớp học không?
mika

Câu trả lời:


8

Tôi sẽ sử dụng mã giả giống như Python dựa trên các trường hợp thử nghiệm của bạn để (hy vọng) giúp giải thích câu trả lời này một chút, thay vì chỉ hiển thị. Nhưng tóm lại: Câu trả lời này về cơ bản chỉ là một ví dụ về đảo ngược phụ thuộc tiêm / phụ thuộc cấp độ đơn chức năng , thay vì áp dụng nguyên tắc này cho toàn bộ lớp / đối tượng.

Ngay bây giờ, có vẻ như các bài kiểm tra của bạn được cấu trúc như vậy:

def test_3_minute_confirm():
    req = make_request()
    sleep(3 * 60)  # Baaaad
    assertTrue(confirm_request(req))

Với việc triển khai một cái gì đó dọc theo dòng:

def make_request():
    return { 'request_time': datetime.now(), }

def confirm_request(req):
    return req['request_time'] >= (datetime.now() - timedelta(minutes=5))

Thay vào đó, hãy thử quên đi "bây giờ" và nói cho cả hai chức năng thời gian sử dụng. Nếu hầu hết thời gian sẽ là "bây giờ", bạn có thể thực hiện một trong hai điều chính để giữ cho mã cuộc gọi đơn giản:

  • Đặt thời gian là một đối số tùy chọn mặc định là "ngay bây giờ". Trong Python (và PHP, bây giờ tôi đọc lại câu hỏi) đây là đơn giản - "thời gian" mặc định thành None( nulltrong PHP) và đặt thành "bây giờ" nếu không có giá trị nào được đưa ra.
  • Trong các ngôn ngữ khác không có đối số mặc định (hoặc trong PHP nếu nó trở nên khó sử dụng), có thể dễ dàng rút ruột của các hàm ra thành một trình trợ giúp được kiểm tra.

Ví dụ, hai chức năng trên được viết lại trong phiên bản thứ hai có thể trông như thế này:

def make_request():
    return make_request_at(datetime.now())

def make_request_at(when):
    return { 'request_time': when, }

def confirm_request(req):
    return confirm_request_at(req, datetime.now())

def confirm_request_at(req, check_time):
    return req['request_time'] >= (check_time - timedelta(minutes=5))

Theo cách này, các phần hiện tại của mã không cần phải sửa đổi để chuyển vào "ngay bây giờ", trong khi các phần bạn thực sự muốn kiểm tra có thể được kiểm tra dễ dàng hơn nhiều:

def test_3_minute_confirm():
    current_time = datetime.now()
    later_time = current_time + timedelta(minutes=3)

    req = make_request_at(current_time)
    assertTrue(confirm_request_at(req, later_time))

Nó cũng tạo ra sự linh hoạt bổ sung trong tương lai, do đó ít cần thay đổi nếu các phần quan trọng cần được sử dụng lại. Đối với hai ví dụ xuất hiện trong tâm trí: Một hàng đợi trong đó các yêu cầu có thể cần được tạo ra quá muộn nhưng vẫn sử dụng đúng thời gian hoặc xác nhận phải xảy ra đối với các yêu cầu trong quá khứ như là một phần của kiểm tra độ tỉnh táo dữ liệu.

Về mặt kỹ thuật, suy nghĩ trước như thế này có thể vi phạm YAGNI , nhưng chúng tôi không thực sự xây dựng cho những trường hợp lý thuyết đó - thay đổi này sẽ chỉ để thử nghiệm dễ dàng hơn, điều đó xảy ra để hỗ trợ những trường hợp lý thuyết đó.


Có bất kỳ lý do nào bạn muốn giới thiệu một trong những giải pháp này hơn một giải pháp khác không?

Cá nhân, tôi cố gắng tránh mọi kiểu chế nhạo càng nhiều càng tốt, và thích các chức năng thuần túy như trên. Theo cách này, mã đang được kiểm tra giống hệt với mã được chạy bên ngoài các kiểm tra, thay vì có các phần quan trọng được thay thế bằng các giả.

Chúng tôi đã có một hoặc hai hồi quy mà không ai nhận thấy trong một tháng tốt vì phần mã cụ thể đó thường không được kiểm tra trong quá trình phát triển (chúng tôi không tích cực làm việc với nó), đã bị phát hành một chút (vì vậy không có lỗi báo cáo) và các giả được sử dụng đã che giấu một lỗi trong sự tương tác giữa các chức năng của trình trợ giúp. Một vài sửa đổi nhỏ để các giả thiết không cần thiết và có thể dễ dàng kiểm tra nhiều hơn, bao gồm sửa các thử nghiệm xung quanh hồi quy đó.

Thay đổi ngày và giờ hệ thống để kiểm tra, sử dụng exec (). Điều này có thể giải quyết nếu bạn không sợ làm hỏng những thứ khác trên máy chủ.

Đây là một trong những điều nên tránh bằng mọi giá cho thử nghiệm đơn vị. Thực sự không có cách nào để biết những loại mâu thuẫn hoặc điều kiện chủng tộc nào có thể được đưa ra để gây ra lỗi không liên tục (đối với các ứng dụng không liên quan hoặc đồng nghiệp trên cùng một hộp phát triển) và thử nghiệm thất bại / thất bại có thể không đặt lại đồng hồ chính xác.


Trường hợp rất tốt cho tiêm phụ thuộc. Cảm ơn các chi tiết, nó thực sự giúp để có được toàn bộ hình ảnh.
mika

8

Đối với tiền của tôi, giữ một sự phụ thuộc vào một số lớp đồng hồ là cách rõ ràng nhất để xử lý việc kiểm tra phụ thuộc thời gian. Trong PHP, nó có thể đơn giản như một lớp aa với một hàm duy nhất nowtrả về thời gian hiện tại bằng cách sử dụng time()hoặc new DateTime().

Việc tiêm phụ thuộc này cho phép bạn thao tác thời gian trong các thử nghiệm của mình và sử dụng thời gian thực trong sản xuất mà không phải viết quá nhiều mã.


Đây là những gì đang chạy trong hộp của tôi :) Và nó đã hoạt động khá tốt. Tất nhiên, bạn phải viết mã suy nghĩ về lớp học thêm của bạn, đây là chi phí.
mika

1
@Mika: Chi phí tối thiểu là tối thiểu, vì vậy nó không quá tệ. Tuy nhiên, tôi sẽ nói rằng sở thích thực sự của tôi là truyền ngày bắt buộc \ lần \ datetimes làm tham số cho một phương thức.
Andy Hunt

3

Điều đầu tiên bạn cần làm là loại bỏ các số ma thuật khỏi mã của bạn. Trong trường hợp này, số ma thuật "5 phút" của bạn. Bạn không chỉ chắc chắn sẽ cần phải thay đổi những điều này sau này khi một số khách hàng yêu cầu, nó làm cho cách kiểm tra đơn vị tốt hơn. Ai sẽ đợi 5 phút để bài kiểm tra của họ chạy?

Thứ hai, nếu bạn chưa có, hãy sử dụng UTC cho dấu thời gian thực tế. Điều này sẽ ngăn chặn các vấn đề với tiết kiệm ánh sáng ban ngày và hầu hết các trục trặc đồng hồ khác.

Trong thử nghiệm đơn vị, chuyển thời lượng được định cấu hình thành khoảng 500ms và thực hiện kiểm tra bình thường của bạn: nó có hoạt động theo một cách trước khi hết thời gian và một cách khác sau đó.

~ 1s vẫn còn khá chậm đối với các bài kiểm tra đơn vị và có thể bạn sẽ cần một số ngưỡng để tính đến hiệu suất của máy và sự thay đổi đồng hồ do NTP (hoặc độ trôi khác). Nhưng theo kinh nghiệm của tôi, đây là cách thực tế tốt nhất để đơn vị kiểm tra thời gian dựa trên công cụ trong hầu hết các hệ thống. Đơn giản, nhanh chóng, đáng tin cậy. Hầu hết các phương pháp khác (như bạn đã tìm thấy) đòi hỏi rất nhiều công việc và / hoặc dễ bị lỗi nghiêm trọng.


Đây là một cách tiếp cận thú vị. "5" đứng ở đây vì lợi ích của ví dụ, tôi luôn đảm bảo rằng tôi có thể thay đổi nó trong một tệp cấu hình :)
mika

@mika - Cá nhân tôi có xu hướng ghét các tập tin. Họ không chơi đẹp với các bài kiểm tra đơn vị (cần có tính đồng thời cao).
Telastyn

Tôi có xu hướng sử dụng yaml khá rộng rãi ...
mika

3

Khi tôi mới học TDD, tôi đã làm việc trong môi trường . Cách tôi học để làm điều đó là có một môi trường IoC hoàn chỉnh, bao gồm một ITimeService, chịu trách nhiệm cho tất cả các dịch vụ thời gian và có thể dễ dàng bị chế giễu trong các thử nghiệm.

Ngày nay tôi đang làm việc trong , và thời gian khai thác dễ dàng hơn rất nhiều - bạn chỉ đơn giản là thời gian sơ khai :

describe 'some time based module' do

  before(:each) do
    @now = Time.now
    allow(Time).to receive(:now) { @now }
  end

  it 'times out after five minutes' do
    subject.do_something

    @now += 5.minutes

    expect { subject.do_another_thing }.to raise TimeoutError
  end
end

1
A, ma thuật ruby! Mặc dù vậy, tôi chỉ cần quay lại cuốn sách của mình để nhận mã đầy đủ;) Sơ khai làm việc rất thanh lịch trong nhiều trường hợp.
mika

3

Sử dụng Microsoft Fakes - thay vì thay đổi mã của bạn cho phù hợp với công cụ, công cụ sẽ thay đổi mã của bạn khi chạy, tiêm một đối tượng Thời gian mới (mà bạn xác định trong thử nghiệm của mình) thay cho đồng hồ tiêu chuẩn.

Nếu bạn đang chạy một ngôn ngữ động - thì Runkit chỉ là cùng một loại.

Ví dụ với hàng giả:

[TestMethod]
public void MyDateProvider_ShouldReturnDateAsJanFirst1970()
{
    using (Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create())
    {
        // Arrange: set up a shim to our own date object instead of System.DateTime.Now
        System.Fakes.ShimDateTime.NowGet = () => new DateTime(1970, 1, 1);

        //Act: call the method under test
        var curentDate = MyDateTimeProvider.GetTheCurrentDate();

        //Assert: that we have a moustache and a brown suit.
        Assert.IsTrue(curentDate == new DateTime(1970, 1, 1));
    }
}

public class MyDateTimeProvider
{
    public static DateTime GetTheCurrentDate()
    {
        return DateTime.Now;
    }
}

Vì vậy, khi phương thức văn bản gọi return DateTime.Nownó sẽ trả về thời gian bạn đã tiêm, thay vì thời gian thực hiện tại.


Fakes, Runkit ... Cuối cùng, mọi ngôn ngữ đều có cách để xây dựng sơ khai. Bạn có thể đăng một ví dụ ngắn để chúng tôi có thể cảm nhận được nó diễn ra như thế nào không?
mika

3

Tôi đã khám phá tùy chọn không gian tên trong PHP. Không gian tên cho chúng ta cơ hội để thêm một số hành vi kiểm tra vào chức năng DateTime. Do đó, các đối tượng ban đầu của chúng tôi không bị ảnh hưởng.

Đầu tiên, chúng tôi thiết lập một đối tượng DateTime giả trong không gian tên để chúng tôi có thể, trong các thử nghiệm của mình, quản lý thời gian được coi là "bây giờ".

Sau đó, trong các thử nghiệm của mình, chúng tôi có thể quản lý thời gian này thông qua các hàm tĩnh DateTime :: set_now ($ my_time) và DateTime :: redirect_now ("thêm một chút thời gian").

Đầu tiên là một đối tượng đặt phòng giả mạo . Đối tượng này là những gì chúng tôi muốn kiểm tra - quan sát việc sử dụng DateTime () mới mà không có tham số ở hai nơi. Một tổng quan ngắn:

<?php

namespace BookingNamespace;
/**
* Simulate expiration process with two public methods:
*      - book will return a token
*      - confirm will take a token, and return
*                       -> true if called within 5 minutes of corresponding book call
*                       -> false if more than 5 minutes have passed
*/
class MyBookingObject
{
    ...

    private function init_token($token){ $this->tokens[$token] = new DateTime(); }

    ...

    private function has_timed_out(DateTime $booking_time)
    {
            $now = new DateTime();
            ....
    }
}

Đối tượng DateTime của chúng tôi ghi đè đối tượng DateTime cốt lõi trông như thế này . Tất cả các cuộc gọi chức năng vẫn không thay đổi. Chúng tôi chỉ "móc" nhà xây dựng để mô phỏng hành vi "bây giờ là bất cứ khi nào tôi chọn". Quan trọng nhất,

namespace BookingNamespace;

/**
 * extend DateTime functionalities with static functions so that we are able 
 * to define what should be considered to be "now"
 *   - set_now allows to define "now"
 *   - modify_now applies the modify function on what is defined as "now"
 *   - reset is for going back to original DateTime behaviour
 *  
 * @author mika
 */
 class DateTime extends \DateTime
 {
     private static $fake_time=null;

     ...

     public function __construct($time = "now", DateTimeZone $timezone = null)
     {
          if($this->is_virtual()&&$this->is_relative_date($time))
          {
           ....
          }
          else
            parent::__construct($time, $timezone);
     }
 }

Các xét nghiệm của chúng tôi sau đó rất đơn giản. Nó giống như thế này với đơn vị php (phiên bản đầy đủ ở đây ):

....
require_once "DateTime.class.php"; // Override DateTime in current namespace
....

class MyBookingObjectTest extends \PHPUnit_Framework_TestCase
{
    ....

    /**
     * Create test subject before test
     */
    protected function setUp()
    {
        ....
        DateTime::set_now("2014-04-02 16:15");
        ....
    }

   ...

    public function testBookAndConfirmThreeMinutesLater()
    {
        $token = $this->booking_object->book();
        DateTime::modify_now("+3 minutes");
        $this->assertTrue($this->booking_object->confirm($token));
    }

    ...
}

Cấu trúc này có thể dễ dàng được sao chép bất cứ nơi nào tôi cần quản lý "ngay bây giờ" theo cách thủ công: Tôi chỉ cần bao gồm đối tượng DateTime mới và sử dụng các phương thức tĩnh của nó trong các thử nghiệm của mình.


Đặt một phiên bản DateTime giả định để kiểm tra lớp mục tiêu sẽ là một kế hoạch tốt hơn, nhưng cần một chút thay đổi trên lớp mục tiêu: đặt new Datetimetrong phương thức riêng biệt để có thể giả định được.
Fwolf 27/2/2015
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.