c# - 如何对依赖于 App 实例的方法进行单元测试

标签 c# unit-testing xamarin.forms

我的 ItemsViewModel 中有一个方法 LoadItems(),它应该使用 SQLite 数据库中的项目填充 View 模型的属性。

我如何对该方法进行单元测试以确保我的 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);
    }
}

最佳答案

This is what Dependency Injection is all about : 不要将其他类型的 static 成员用于外部服务,如 IO(数据库等)。

(而且你的数据库操作应该是async)

将您的类(class)更改为:

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 为测试创建或作为 IClassFixture 而无需使用 MyViewModelLocator(请注意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 );
    }
}

关于c# - 如何对依赖于 App 实例的方法进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57347104/

相关文章:

azure - 将 Microsoft.WindowsAzure.Mobile.SQLStore 迁移到 Microsoft.Azure.Mobile.Client.SQLiteStore

c# - GridView 中 DropDownList 中的 Eval()

c# - 控制台应用程序宿主wcf服务交互

unit-testing - Grails-使用reCaptcha插件对 Controller 的方法进行单元测试

java - 如何使用 Java 启动和停止 Tomcat 容器?

C# 项目文件 - 为什么它不代表我项目中的内容?

c# - Xamarin 表格 : Embed symbols in text

c# - 将字典与列表绑定(bind)的优雅方式

c# - 在单一方法中根据 XSD 验证 XML

java - 对文件存在的不同行为进行单元测试