Có nhiều cách khác nhau để giải quyết vấn đề này.
phiên bản Imperative
Dưới đây là một phiên bản bắt buộc đơn giản hơn so với cái được cung cấp trong OP:
[Fact]
public void ImperativeTest()
{
var fixture = new Fixture();
var expected = fixture.CreateMany<SyncItem>(3).OrderBy(si => si.Key);
var unorderedItems = expected.Skip(1).Concat(expected.Take(1)).ToArray();
fixture.Inject(unorderedItems);
var sut = fixture.Create<SyncItemList>();
Assert.Equal(expected, sut);
}
Trong khi the default number of many items is 3, tôi nghĩ rằng nó tốt hơn để gọi nó ra một cách rõ ràng trong trường hợp thử nghiệm này. Thuật toán xáo trộn được sử dụng ở đây tận dụng thực tế là sau khi đặt hàng một chuỗi gồm ba phần tử (khác biệt), di chuyển phần tử đầu tiên về phía sau phải dẫn đến danh sách không có thứ tự.
Tuy nhiên, vấn đề với cách tiếp cận này là nó dựa vào biến đổi fixture
, do đó rất khó để tái cấu trúc một cách tiếp cận khai báo hơn.
tùy chỉnh phiên bản
Trong một nỗ lực nhằm cấu trúc lại một phiên bản declarative hơn, trước tiên bạn có thể gói gọn các thuật toán xáo trộn trong a Customization:
public class UnorderedSyncItems : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new UnorderedSyncItemsGenerator());
}
private class UnorderedSyncItemsGenerator : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var t = request as Type;
if (t == null ||
t != typeof(SyncItem[]))
return new NoSpecimen(request);
var items = ((IEnumerable)context
.Resolve(new FiniteSequenceRequest(typeof(SyncItem), 3)))
.Cast<SyncItem>();
return items.Skip(1).Concat(items.Take(1)).ToArray();
}
}
}
Giải quyết new FiniteSequenceRequest(typeof(SyncItem), 3))
chỉ đơn giản là một cách yếu ớt, đánh máy (không chung chung) tạo ra một chuỗi hữu hạn gồm SyncItem
trường hợp; đó là những gì CreateMany<SyncItem>(3)
làm sau hậu trường.
này cho phép bạn cấu trúc lại các thử nghiệm để:
[Fact]
public void ImperativeTestWithCustomization()
{
var fixture = new Fixture().Customize(new UnorderedSyncItems());
var expected = fixture.Freeze<SyncItem[]>().OrderBy(si => si.Key);
var sut = fixture.Create<SyncItemList>();
Assert.Equal(expected, sut);
}
Thông báo việc sử dụng các phương pháp Freeze. Điều này là cần thiết, vì tùy chọn UnorderedSyncItems
chỉ thay đổi cách thức SyncItem[]
phiên bản được tạo; nó vẫn tạo ra một mảng mới mỗi khi nó nhận được một yêu cầu để làm như vậy. Freeze
đảm bảo rằng cùng một mảng đang được sử dụng lại mọi lúc - cũng như khi fixture
tạo phiên bản sut
.
thử nghiệm Công ước dựa trên
Các thử nghiệm trên có thể được refactored để một, kiểm tra ước dựa trên khai báo, bởi sự ra đời của một [UnorderedConventions]
thuộc tính:
public class UnorderedConventionsAttribute : AutoDataAttribute
{
public UnorderedConventionsAttribute()
: base(new Fixture().Customize(new UnorderedSyncItems()))
{
}
}
này chỉ đơn giản là keo declarative để áp dụng tùy chọn UnorderedSyncItems
. Thử nghiệm hiện tại trở thành:
[Theory, UnorderedConventions]
public void ConventionBasedTest(
[Frozen]SyncItem[] unorderedItems,
SyncItemList sut)
{
var expected = unorderedItems.OrderBy(si => si.Key);
Assert.Equal(expected, sut);
}
Lưu ý việc sử dụng thuộc tính [UnorderedSyncItems]
và [Frozen]
.
Bài kiểm tra này rất ngắn gọn, nhưng có thể không phải là những gì bạn đang theo dõi. Vấn đề là sự thay đổi của hành vi bây giờ được ẩn đi trong thuộc tính [UnorderedSyncItems]
, do đó, nó khá tiềm ẩn những gì đang xảy ra. Tôi thích sử dụng cùng một Tùy chỉnh như một tập hợp các quy ước cho toàn bộ bộ thử nghiệm, vì vậy tôi không muốn giới thiệu các biến thể của trường hợp thử nghiệm ở cấp độ này. Tuy nhiên, nếu quy ước của bạn nêu rõ rằng SyncItem[]
trường hợp nên luôn là không có thứ tự, thì quy ước này là tốt.
Tuy nhiên, nếu bạn chỉ muốn sử dụng mảng không theo thứ tự cho một số trường hợp thử nghiệm, việc sử dụng thuộc tính [AutoData]
không phải là cách tiếp cận tối ưu nhất.
test Declarative
Nó muốn được tốt đẹp nếu bạn chỉ đơn giản có thể áp dụng một thuộc tính tham số cấp, giống như thuộc tính [Frozen]
- có lẽ kết hợp chúng, như [Unordered][Frozen]
. Tuy nhiên, cách tiếp cận này không hiệu quả.
Lưu ý từ các ví dụ trước rằng các yêu cầu đơn hàng. Bạn phải áp dụng UnorderedSyncItems
trước khi đóng băng, vì nếu không, mảng đang được cố định có thể không được đảm bảo không có thứ tự.
Sự cố với thuộc tính mức tham số [Unordered][Frozen]
là, trong khi biên dịch. Khuôn khổ .NET không đảm bảo thứ tự các thuộc tính khi thư viện kết dính Tự động xUnit.net đọc và áp dụng các thuộc tính.
Thay vào đó, bạn có thể định nghĩa một thuộc tính duy nhất để áp dụng, như thế này:
public class UnorderedFrozenAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new CompositeCustomization(
new UnorderedSyncItems(),
new FreezingCustomization(parameter.ParameterType));
}
}
(FreezingCustomization
cung cấp việc thực hiện cơ bản của thuộc tính [Frozen]
.)
này cho phép bạn viết bài kiểm tra này:
[Theory, AutoData]
public void DeclarativeTest(
[UnorderedFrozen]SyncItem[] unorderedItems,
SyncItemList sut)
{
var expected = unorderedItems.OrderBy(si => si.Key);
Assert.Equal(expected, sut);
}
Lưu ý rằng thử nghiệm khai báo này sử dụng thuộc tính mặc định [AutoData]
không có bất kỳ Customi nào zations, bởi vì sự xáo trộn hiện đang được áp dụng bởi thuộc tính [UnorderedFrozen]
ở cấp tham số.
Điều này cũng sẽ cho phép bạn sử dụng một tập hợp (các) quy ước toàn bộ thử nghiệm khác được đóng gói trong thuộc tính [AutoData]
-derived và vẫn sử dụng [UnorderedFrozen]
làm cơ chế chọn tham gia.
Hình thức 'SyncItemList' trông như thế nào? –
Bạn biết điều gì sẽ thực sự tuyệt vời nếu có thuộc tính và loại yêu cầu không được sắp xếp, giống như chuỗi hữu hạn. Bạn sẽ chỉ cần 3 hoặc nhiều hơn các mặt hàng độc đáo và bạn có thể đảm bảo một chuỗi thứ tự. – cocogorilla
Tôi có thể hỏi bạn đang sử dụng phiên bản AutoFixture nào không? [Số ngẫu nhiên] (https://github.com/AutoFixture/AutoFixture/wiki/AutoFixture-3.0-Release-Notes#numbers-are-random) trong AutoFixture 3. –