C# 如何依赖应用程序实例对方法进行单元测试

C# 如何依赖应用程序实例对方法进行单元测试,c#,unit-testing,xamarin.forms,C#,Unit Testing,Xamarin.forms,在我的ItemsViewModel中有一个方法LoadItems(),它应该用SQLite数据库中的项填充视图模型的属性 如何对该方法进行单元测试,以确保使用数据库中的内容正确更新my Items属性 我想我必须以某种方式模拟数据库,但如果不重写LoadItems()方法本身,我就不知道如何进行模拟 public class ItemsViewModel : BaseViewModel { public ObservableCollection<Item> Items { g

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

如何对该方法进行单元测试,以确保使用数据库中的内容正确更新my 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);
    }
}
public类ItemsViewModel:BaseViewModel
{
公共ObservableCollection项{get;set;}
public ItemsViewModel(){…}
void LoadItems()
{
Items.Clear();
var items=App.Database.GetItems();
foreach(项目中的var项目)
项目。添加(项目);
}
}
:不要将其他类型的
静态
成员用于IO(数据库等)等外部服务

(您的数据库操作应该是
异步的

将您的类更改为:

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)
    }
}
然后,为了测试它,您可以提供自己的
IDatabase
,它可以是为测试而创建的,也可以是作为
IClassFixture
而不使用
MyViewModelLocator
(请注意,xUnit的
IClassFixture
与DI不同):

或:

公共类项目测试:IClassFixture
{
私有只读IDatabase数据库;
公共项目测试(IDatabase db)
{
this.db=db??抛出新的ArgumentNullException(nameof(db));
}
[事实]
公共异步任务加载\u项\u通过\u ronseal\u质询()
{
ItemsViewModel vm=新的ItemsViewModel(this.db);
Assert.Equal(0,vm.Items.Count);
等待vm.LoadItemsAsync();
Assert.Equal(5,vm.Items.Count);
}
}
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();
}
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 );
    }
}