forked from thomhurst/TUnit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPropertyDataSourceInjectionTests.cs
More file actions
275 lines (230 loc) · 8.83 KB
/
Copy pathPropertyDataSourceInjectionTests.cs
File metadata and controls
275 lines (230 loc) · 8.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
using System.Diagnostics.CodeAnalysis;
using TUnit.Core.Interfaces;
using TUnit.Core.Helpers;
namespace TUnit.UnitTests;
public class PropertyDataSourceInjectionTests
{
// Property with Arguments attribute
[Arguments("injected value")]
public required string? StringProperty { get; set; }
// Property with MethodDataSource
[MethodDataSource(nameof(GetTestData))]
public required TestData? DataProperty { get; set; }
// Property with ClassDataSource
[ClassDataSource<TestDataProvider>]
public required ITestDataProvider? DataProviderProperty { get; set; }
// Property with Arguments (complex type support)
[Arguments("injected value")] // Fix: Use simple string instead of incompatible types
public required string? ComplexProperty { get; set; }
[Test]
public async Task PropertyInjection_ArgumentsAttribute_InjectsValue()
{
await Assert.That(StringProperty).IsNotNull();
await Assert.That(StringProperty).IsEqualTo("injected value");
}
[Test]
public async Task PropertyInjection_MethodDataSource_InjectsValue()
{
await Assert.That(DataProperty).IsNotNull();
await Assert.That(DataProperty!.Value).IsEqualTo("test data");
}
[Test]
public async Task PropertyInjection_ClassDataSource_InjectsAndInitializes()
{
await Assert.That(DataProviderProperty).IsNotNull();
await Assert.That(DataProviderProperty!.IsInitialized).IsTrue();
await Assert.That(DataProviderProperty.GetData()).IsEqualTo("initialized data");
}
[Test]
public async Task PropertyInjection_ArgumentsAttribute_ComplexType()
{
// Simple string property injection should work
await Assert.That(ComplexProperty).IsEqualTo("injected value");
}
// Helper methods and types
public static TestData GetTestData()
{
return new TestData { Value = "test data" };
}
public class TestData
{
public string Value { get; set; } = "";
}
public interface ITestDataProvider
{
bool IsInitialized { get; }
string GetData();
}
public class TestDataProvider : ITestDataProvider, IAsyncInitializer
{
public bool IsInitialized { get; private set; }
public async Task InitializeAsync()
{
await Task.Delay(1);
IsInitialized = true;
}
public string GetData()
{
return IsInitialized ? "initialized data" : "not initialized";
}
}
public class ComplexData
{
public int Id { get; set; }
public string Name { get; set; } = "";
// Nested property with data source
[Arguments("nested value")]
public required string? NestedProperty { get; set; }
}
}
// Test for inheritance
public class BaseTestClass
{
[Arguments("base value")]
public required string? BaseProperty { get; set; }
}
public class DerivedPropertyInjectionTests : BaseTestClass
{
[Arguments("derived value")]
public required string? DerivedProperty { get; set; }
[Test]
public async Task PropertyInjection_Inheritance_InjectsBaseAndDerivedProperties()
{
await Assert.That(BaseProperty).IsEqualTo("base value");
await Assert.That(DerivedProperty).IsEqualTo("derived value");
}
}
// Test for custom data source attribute
public class CustomPropertyDataSourceTests
{
[CustomDataSource<CustomService>]
public required CustomService? Service { get; set; }
[Test]
public async Task PropertyInjection_CustomDataSource_WorksWithGenericApproach()
{
await Assert.That(Service).IsNotNull();
await Assert.That(Service!.IsInitialized).IsTrue();
await Assert.That(Service.GetMessage()).IsEqualTo("Custom service initialized");
}
[Test]
public async Task PropertyInjection_CustomDataSource_WithNestedProperties_InjectsAndInitializesRecursively()
{
// Test main service
await Assert.That(Service).IsNotNull();
await Assert.That(Service!.IsInitialized).IsTrue();
await Assert.That(Service.GetMessage()).IsEqualTo("Custom service initialized");
// Test nested service
await Assert.That(Service.NestedService).IsNotNull();
await Assert.That(Service.NestedService!.IsInitialized).IsTrue();
await Assert.That(Service.NestedService.GetData()).IsEqualTo("Nested service initialized");
// Test deeply nested service
await Assert.That(Service.NestedService.DeeplyNestedService).IsNotNull();
await Assert.That(Service.NestedService.DeeplyNestedService!.IsInitialized).IsTrue();
await Assert.That(Service.NestedService.DeeplyNestedService.GetDeepData()).IsEqualTo("Deeply nested service initialized");
}
}
// Custom data source attribute that inherits from AsyncDataSourceGeneratorAttribute
public class CustomDataSourceAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> : AsyncDataSourceGeneratorAttribute<T>
{
protected override async IAsyncEnumerable<Func<Task<T>>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata)
{
yield return async () =>
{
// Use the DataSourceHelpers to create objects with init-only properties properly
var (success, createdInstance) = await DataSourceHelpers.TryCreateWithInitializerAsync(typeof(T), dataGeneratorMetadata.TestInformation, dataGeneratorMetadata.TestSessionId);
if (success)
{
return (T)createdInstance!;
}
// Fallback to regular Activator if no specialized creator is available
return (T)Activator.CreateInstance(typeof(T))!;
};
await Task.CompletedTask;
}
}
public class CustomService : IAsyncInitializer
{
public bool IsInitialized { get; private set; }
// Nested property with its own data source
[CustomDataSource<NestedService>]
public required NestedService? NestedService { get; set; }
public async Task InitializeAsync()
{
await Task.Delay(1);
IsInitialized = true;
}
public string GetMessage()
{
return IsInitialized ? "Custom service initialized" : "Not initialized";
}
}
public class NestedService : IAsyncInitializer
{
public bool IsInitialized { get; private set; }
// Deeply nested property with its own data source
[CustomDataSource<DeeplyNestedService>]
public required DeeplyNestedService? DeeplyNestedService { get; set; }
public async Task InitializeAsync()
{
await Task.Delay(1);
IsInitialized = true;
}
public string GetData()
{
return IsInitialized ? "Nested service initialized" : "Nested not initialized";
}
}
public class DeeplyNestedService : IAsyncInitializer
{
public bool IsInitialized { get; private set; }
public async Task InitializeAsync()
{
await Task.Delay(1);
IsInitialized = true;
}
public string GetDeepData()
{
return IsInitialized ? "Deeply nested service initialized" : "Deeply nested not initialized";
}
}
// Regression test for Issue #3991: TestContext.Current is null during property injection
public class TestContextAvailabilityDuringPropertyInjectionTests
{
[TestContextDataSource]
public required TestContextCapture? ContextCapture { get; set; }
[Test]
public async Task PropertyInjection_CanAccessTestContext_DuringDiscoveryPhase()
{
// This test verifies that TestContext.Current is available during property injection
// in the discovery/registration phase (Issue #3991)
await Assert.That(ContextCapture).IsNotNull();
await Assert.That(ContextCapture!.TestContextWasAvailable).IsTrue();
await Assert.That(ContextCapture.CapturedTestContextId).IsNotNull();
}
}
// Data source that accesses TestContext.Current during initialization
// This reproduces the bug from Issue #3991
public class TestContextDataSourceAttribute : AsyncDataSourceGeneratorAttribute<TestContextCapture>
{
protected override async IAsyncEnumerable<Func<Task<TestContextCapture>>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata)
{
yield return async () =>
{
// This is where the bug occurs: TestContext.Current is null during discovery
var testContext = TestContext.Current;
return new TestContextCapture
{
TestContextWasAvailable = testContext != null,
CapturedTestContextId = testContext?.Id,
CapturedCancellationToken = testContext?.CancellationToken ?? default
};
};
await Task.CompletedTask;
}
}
public class TestContextCapture
{
public bool TestContextWasAvailable { get; set; }
public string? CapturedTestContextId { get; set; }
public CancellationToken CapturedCancellationToken { get; set; }
}