简体   繁体   中英

How should I test this?

I have a ViewModel, which when initialized, creates a list of objects. These objects are created by resolving interface implementation from IoC.

ViewModel

    public class ApplianceViewModel : IAppliance
    {
        private List<IHardware> HardwareItems {get; set;}

        public ApplianceViewModel()
        {
            HardwareItems = new List<IHardware>();
            var hardware1 = Constants.Kernel.Get<IHardware>();
            var hardware2 = Constants.Kernel.Get<IHardware>();

            HardwareItems.Add(hardware1);
            HardwareItems.Add(hardware2);           
        }
    }

Unit Test

[TestClass]
public class ApplicanceViewModelTest
{
    [TestMethod]
    public void TestSomething()
    {
        //Arrange
        var appliance = new ApplianceViewModel(); //This would fail to construct

    }
}

The above initialization would fail as the kernel would be null. Basically, it tells me that having a constant class and accessing kernel directly is a bad design.

But then how would I modify my viewmodel 's constructor so that I can initialize a list, in other words make it testable?

Thank you.

But then how would I modify my viewmodel's constructor so that I can initialize a list, in other words make it testable?

You inject it with implementations of the IHardware interface:

public class ApplianceViewModel : IAppliance
{
    private List<IHardware> HardwareItems {get; set;}

    public ApplianceViewModel(IHardware hardware1, Hardware hardware2)
    {
        HardwareItems = new List<IHardware>();
        var hardware1 = hardware1;
        var hardware2 = hardware2;

        HardwareItems.Add(hardware1);
        HardwareItems.Add(hardware2);           
    }
}

So at runtime you could inject the view model with your existing constants:

var appliance = new ApplianceViewModel(Constants.Kernel.Get<IHardware>(), Constants.Kernel.Get<IHardware>());

...and in your unit test project you pass in some other implemenation of the same interface:

IHardware testHardware = new TestHardware();
var appliance = new ApplianceViewModel(testHardware, testHardware);

You will need to implement this "TestHardware" class in a way that makes your test pass. Using a mocking framework will be useful. Please refer to the following link for more information about this: http://codetunnel.io/what-is-a-mocking-framework-why-is-it-useful/

You can have another interface, ie IHardwareStuff , that will return a list of IHardware objects. The IHardwareStuff gets passed as an argument into the constructor of your ViewModel. It's resolved by the dependency injection container and you can input a test implementation when you want to.

By doing this separation, you split your object into a value object and a service object . The first one you create by doing new and passing some concrete values to it. The latter is created by the dependency injection container and doesn't have concrete values passed into it as a constructor.

You can than write unit tests for both objects.

For a good and testable design it's better if constructors do nothing, validation or variable assignment.

I suggesto to inject in the constructor of ApplianceViewModel the list HardwareItems .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM