-
In my blazor component i use Typically i would implement services like it's shown here: https://bunit.dev/docs/providing-input/inject-services-into-components.html But i have no clue how to implement ProtectedSessionStorage. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 12 replies
-
Hey @newbiE247 , The link you provided does not take me anywhere. When implementing ProtectedSessionStorage, I would hide the storage behind an interface and then mock the dependency. |
Beta Was this translation helpful? Give feedback.
-
hi @newbiE247, moved this to QA instead of an issue. Anyway, the First we create an abstraction that has the same API surface as [Inject] public IBrowserStorage SessionStorage { get; set; } Here is the public interface IBrowserStorage
{
ValueTask SetAsync(string key, object value);
ValueTask SetAsync(string purpose, string key, object value);
ValueTask<StorageResult<TValue>> GetAsync<TValue>(string key);
ValueTask<StorageResult<TValue>> GetAsync<TValue>(string purpose, string key);
ValueTask DeleteAsync(string key);
} Since public readonly struct StorageResult<TValue>
{
/// <summary>
/// Gets whether the operation succeeded.
/// </summary>
public bool Success { get; }
/// <summary>
/// Gets the result value of the operation.
/// </summary>
public TValue? Value { get; }
public StorageResult(bool success, TValue? value)
{
Success = success;
Value = value;
}
public StorageResult(in ProtectedBrowserStorageResult<TValue> result)
{
Success = result.Success;
Value = result.Value;
}
} Now we need to create two implementations, one for production use, and a fake implementation for testing purposes. Here is the one for production use, lets name it public class BrowserStorage : IBrowserStorage
{
private readonly ProtectedBrowserStorage browserStorage;
public BrowserStorage(ProtectedBrowserStorage browserStorage) => this.browserStorage = browserStorage;
public ValueTask DeleteAsync(string key) => browserStorage.DeleteAsync(key);
public async ValueTask<StorageResult<TValue>> GetAsync<TValue>(string key)
=> new StorageResult<TValue>(await browserStorage.GetAsync<TValue>(key));
public async ValueTask<StorageResult<TValue>> GetAsync<TValue>(string purpose, string key)
=> new StorageResult<TValue>(await browserStorage.GetAsync<TValue>(purpose, key));
public ValueTask SetAsync(string key, object value) => browserStorage.SetAsync(key, value);
public ValueTask SetAsync(string purpose, string key, object value) => browserStorage.SetAsync(purpose, key, value);
} To use this in your Blazor app, update your builder.Services.AddScoped<IBrowserStorage>(s => new BrowserStorage(s.GetRequiredService<ProtectedSessionStorage>())); Now, we just need the fake version, lets call it public class FakeBrowserStorage : IBrowserStorage
{
private Dictionary<(string Key, string Purpose), object> storage = new();
public ValueTask DeleteAsync(string key)
{
storage.Remove((key, string.Empty));
return ValueTask.CompletedTask;
}
public ValueTask<StorageResult<TValue>> GetAsync<TValue>(string key)
=> GetAsync<TValue>(string.Empty, key);
public ValueTask<StorageResult<TValue>> GetAsync<TValue>(string purpose, string key)
{
var found = storage.TryGetValue((key, purpose), out var objValue);
return found
? ValueTask.FromResult(new StorageResult<TValue>(found, (TValue)objValue))
: ValueTask.FromResult(new StorageResult<TValue>(found, default(TValue)));
}
public ValueTask SetAsync(string key, object value)
=> SetAsync(string.Empty, key, value);
public ValueTask SetAsync(string purpose, string key, object value)
{
storage.Add((key, purpose), value);
return ValueTask.CompletedTask;
}
} To use that in your tests, register it with bUnits services container before rendering a component that uses it: using var ctx = new TestContext();
var fakeStorage= new FakeBrowserStorage();
ctx.Services.AddSingleton<IBrowserStorage>(fakeStorage);
var cut = RenderComponent.... NOTE NOTE: This code above compiles but is not tested. Give it a spin and let me know if it works. |
Beta Was this translation helpful? Give feedback.
-
Another option is to use BlazoredStorage which does come with builtin support for bUnit, and is not in preview like the package from Microsoft, which honestly seems abandoned. |
Beta Was this translation helpful? Give feedback.
-
I see BlazoredStorage use this: I tried something like Here some codesample: public class DataGridTests: TestContext
{
private readonly DokumentRepository dokumentRepository = new();
public DataGridTests()
{
JSInterop.Mode = JSRuntimeMode.Loose;
Services.AddDataProtection();
}
[Fact(DisplayName = "expected to find correct item count after adding items to context")]
public void ExpectToFindCorrectItemCount()
{
// Arrange
var initialItemCount = 5;
var additionalItemCount = 3;
var expectedFinalItemCount = initialItemCount + additionalItemCount;
var context = dokumentRepository.GetSearchResults(initialItemCount);
// Act
var cut = RenderComponent<DataGrid<Dokument>>(parameters => parameters
.Add(p => p.Context, context)
);
var newItems = dokumentRepository.GetSearchResults(additionalItemCount);
context.AddRange(newItems);
cut.Render();
var items = cut.FindComponents<DataGridItem<Dokument>>();
// Assert
Assert.Equal(expectedFinalItemCount, items.Count);
}
} |
Beta Was this translation helpful? Give feedback.
hi @newbiE247, moved this to QA instead of an issue.
Anyway, the
ProtectedSessionStorage
is unfortunately pretty hard to fake or mock in tests without encapsulating it like @Christian-Oleson suggests. Here is some entirely untested code that you can consider using to see if it works for you.First we create an abstraction that has the same API surface as
ProtectedSessionStorage
. Lets call itIBrowserStorage
. That is what your component should have injected into it instead ofProtectedSessionStorage
. E.g. update your components to have this:Here is the
IBrowserStorage
file: