如何对依赖于应用实例的测试方法进行单元测试



ItemsViewModelLoadItems()了一个方法,它应该使用 SQLite 数据库中的项目填充视图模型的属性。

如何对此方法进行单元测试,以确保使用数据库中的内容正确更新 Items 属性?

我想我必须以某种方式模拟数据库,但我无法弄清楚如何在不重写LoadItems()方法本身的情况下做到这一点。

public class ItemsViewModel : BaseViewModel
{
public ObservableCollection<Item> Items { get; set; }
public ItemsViewModel() { ... }
void LoadItems()
{
Items.Clear();
var items = App.Database.GetItems();
foreach (var item in items)
Items.Add(item);
}
}

这就是依赖注入的全部内容:不要将其他类型的static成员用于外部服务,如IO(数据库等)。

(并且您的数据库操作应该async)

将您的类更改为以下内容:

public class ItemsViewModel : BaseViewModel
{
private readonly IDatabase db;
public ItemsViewModel( IDatabase database )
{
this.db = database ?? throw new ArgumentNullException(nameof(database));
this.Items = new ObservableCollection<Item>();
}
public ObservableCollection<Item> Items { get; }
public async Task LoadItemsAsync()
{
// (Show an activity indicator here and disable other inputs)
this.Items.Clear();
var items = await this.db.GetItemsAsync();
this.Items.AddRange( items );
// (Hide the activity indicator here)
}
}

并在 DI 容器中注册数据库:

(我假设您使用的是ViewModelLocator- 尽管许多人认为这是一种反模式):

public static class MyViewModelLocator
{
private static readonly IContainer _container = RegisterDependencies();
private static IContainer RegisterDependencies()
{
return new ContainerBuilder()
// Register services (this is required):
.RegisterType<IDatabase,MyDatabase>()
// Register consumers (this is optional and only needed if you're using the ViewModelLocator pattern in your XAML views):
.RegisterSingleton<ItemsViewModel>()
.Build();
}
public static IContainer Container => _container;
public static ItemsViewModel ItemsViewModel => _container.Resolve<ItemsViewModel();
}

然后要测试它,您需要提供自己的IDatabase,要么是为测试创建的,要么是不使用MyViewModelLocatorIClassFixture(请注意,xUnit 的IClassFixture与 DI 不同):

public class ItemsTests
{
[Fact]
public async Task Load_items_passes_the_ronseal_challenge()
{
using( IDatabase testDatabase = new FakeDatabase() )
{
ItemsViewModel vm = new ItemsViewModel( db );
Assert.Equal( 0, vm.Items.Count );
await vm.LoadItemsAsync();
Assert.Equal( 5, vm.Items.Count );
}
}
}

或:

public class ItemsTests : IClassFixture<IDatabase>
{
private readonly IDatabase db;
public ItemsTests( IDatabase db )
{
this.db = db ?? throw new ArgumentNullException(nameof(db));
}
[Fact]
public async Task Load_items_passes_the_ronseal_challenge()
{
ItemsViewModel vm = new ItemsViewModel( this.db );
Assert.Equal( 0, vm.Items.Count );
await vm.LoadItemsAsync();
Assert.Equal( 5, vm.Items.Count );
}
}

最新更新