从抽象类继承并实现接口的模拟类

假设以下场景:我有一个PhoneController使用Phone类的类。Phone是一个继承自抽象类Device并实现IPhone接口的类。为了测试,PhoneController我想模拟Phone类,但我不知道如何使用 NSubstitute 来完成它,因为Phone类继承了抽象类,并且还实现了接口。


示例代码:


public abstract class Device

{

    protected string Address { get; set; }

}


public interface IPhone

{

    void MakeCall();

}


public class Phone : Device, IPhone

{

    public void MakeCall()

    {

        throw new NotImplementedException();

    }

}


public class PhoneController

{

    private Phone _phone;


    public PhoneController(Phone phone)

    {

        _phone = phone;

    }

}


[TestClass]

public class PhoneControllerTests

{

    [TestMethod]

    public void TestMethod1()

    {

        // How mock Phone class? 

        //var mock = Substitute.For<Device, IPhone>();


        //usage of mock

        //var controller = new PhoneController(mock);

    }

}

第二种情况:

控制器使用抽象类的GetStatus方法Device,因此_phone不能更改为IPhone类型


public abstract class Device

{

    protected string Address { get; set; }


    public abstract string GetStatus();

}


public interface IPhone

{

    void MakeCall();

}


public class Phone : Device, IPhone

{

    public void MakeCall()

    {

        throw new NotImplementedException();

    }


    public override string GetStatus()

    {

        throw new NotImplementedException();

    }

}


public class PhoneController

{

    private Phone _phone;


    public PhoneController(Phone phone)

    {

        _phone = phone;

    }


    public string GetDeviceStatus()

    {

        return _phone.GetStatus();

    }


    public void MakeCall()

    {

        _phone.MakeCall();

    }

}


[TestClass]

public class PhoneControllerTests

{

    [TestMethod]

    public void TestMethod1()

    {

        // How mock Phone class? 

        //var mock = Substitute.For<Device, IPhone>();


        //usage of mock

        //var controller = new PhoneController(mock);

    }

}

C#


蝴蝶刀刀
浏览 192回答 2
2回答

largeQ

如果PhoneController需要 a&nbsp;Phone,那么您将不得不使用 aPhone或 的子类Phone。使用Substitute.For<Device, IPhone>()将生成一个有点像这样的类型:public&nbsp;class&nbsp;Temp&nbsp;:&nbsp;Device,&nbsp;IPhone&nbsp;{&nbsp;...&nbsp;}这与Phone.所以有几个选择。理想情况下,我会考虑@pm_2 的建议,PhoneController即IPhone改为take an&nbsp;。通过让它依赖于接口而不是具体的东西,我们获得了一些灵活性:PhoneController现在可以处理任何依附于IPhone接口的东西,包括模拟IPhone实例。这也意味着我们可以通过其他实现来更改生产代码的行为(人为的示例,可能EncryptedPhone : IPhone是加密调用的 ,而无需更改PhoneController)。如果您确实需要将特定Phone类耦合到PhoneController,那么模拟立即变得更加困难。毕竟,您是在说明“这只适用于这个特定的类”,然后试图让它与另一个类(替代的类)一起使用。对于这种方法,如果您能够创建Phone类的所有相关成员,virtual那么您可以使用Substitute.For<Phone>().&nbsp;如果需要还需要运行一些原有的Phone代码,但替换其他部分,那么你可以使用Substitute.ForPartsOf<Phone>()如@ PM_2建议。请记住,NSubstitute(以及许多其他 .NET 模拟库)不会模拟非virtual成员,因此在模拟类时请记住这一点。最后,值得考虑根本不嘲笑Phone并使用真正的类。如果PhoneController取决于具体Phone实现的细节,那么用假版本测试它不会告诉你它是否有效。相反,如果它只需要一个兼容的接口,那么它是使用替代品的一个很好的候选者。模拟库会自动创建与类一起使用的替代类型,但它们不会自动拥有适合该类型使用的设计。:)编辑第二个场景我认为我之前的答案仍然适用于额外的场景。回顾一下:我的第一种方法是尝试将 与PhoneController特定实现分离;那么我会考虑Phone直接替换(带有关于非virtual方法的免责声明)。而且我始终牢记根本不要嘲笑(这可能应该是第一个选择)。有很多方法可以实现第一个选项。我们可以更新PhoneController以获取一个IDevice(extract interface from&nbsp;Device) 和 an&nbsp;IPhone,使用构造函数PhoneController(IPhone p, IDevice d)。真正的代码可以是:var phone = new Phone();var controller = new PhoneController(phone, phone);虽然测试代码可能是:var phone = Substitute.For<IPhone>();var device = Substitute.For<IDevice>();var testController = new PhoneController(phone, device);// orvar phone = Substitute.For<IPhone, IDevice>();var testController = new PhoneController(phone, (IDevice) phone);或者,我们可以创建IPhone,IDevice然后有:interface IPhoneDevice : IPhone, IDevice { }public class Phone : IPhoneDevice { ... }我以前看到过不鼓励这样的接口继承,因此您可能需要在选择此选项之前先研究一下。您还可以组合这些方法,通过IDevice在上面的示例中替换为您当前的Device基类并模拟它(免责声明:非virtuals)。我认为您需要回答的主要问题是您想如何PhoneController与其依赖项耦合?考虑一下它需要什么具体的依赖关系,以及可以用逻辑接口表达什么。答案将决定您的测试选项。
打开App,查看更多内容
随时随地看视频慕课网APP