如何在没有入口点的情况下测试服务以进行数据测试

时间:2019-06-03 22:40:55

标签: c# unit-testing nunit

我正在学习如何使用Nunit。

我有一个名为“ IMenuService”的接口。该接口具有给定menuType的一种方法,它返回所请求的menuItem列表。

实际上,我已经创建了一个名为“ StaticMenuService”的简单实现,其中包含两个静态菜单:admin / user。 StaticMenuService不需要任何存储库,因为它的数据是经过编码的,因此它的构造函数没有参数。

我将使用两个表示admin /菜单的测试列表来测试“ GetMenu(MenuType type)”方法,但是由于StaticMenuService没有数据入口点,我不知道如何通过这些测试列表。

我认为是修改“ StaticMenuService”构造函数,并添加两个参数(UserMenu,AdminMenu),但在这种情况下,我只为 创建一个测试,我认为这是错误的,不是吗?

我如何使用虚假数据来进行测试?

这就是我开始的

public class MenuServiceTest
    {

        [SetUp]
        public void SetUp()
        {
            List<MenuContainerItem> AdminMenuContainer;
            List<MenuContainerItem> UserMenuContainer;

            MenuPageItem adminPageTest1 = new MenuPageItem()
            {
                Id = "TEST_ADMIN_PAGE_1",
                PageName = "admin test 1",
                Url = "/admin/test1"
            };
            MenuPageItem adminPageTest2 = new MenuPageItem()
            {
                Id = "TEST_ADMIN_PAGE_2",
                PageName = "admin test 2",
                Url = "/admin/test2"
            };

            MenuContainerItem AdminBasePagesTest = new MenuContainerItem()
            {
                Id = "ADMIN_CONTAINER_TEST",
                Icon = "preferences",
                ContainerName = "container test",
                Pages = new List<MenuPageItem>() { adminPageTest1 , adminPageTest2}
            };

            AdminMenuContainerTest = new List<MenuContainerItem>() { AdminBasePagesTest };
            UserMenuContainerTest = new List<MenuContainerItem>();


        }

        [TestCase(MenuType.ADMIN)]
        [TestCase(MenuType.USER)]
        public void IMenuServiceReturnsAlwaysAList(MenuType type)
        {
            //var mockData = new Mock<>
            //how can I pass AdminMenuContainerTest and UserMenuContainerTest to an instance of StaticMenuService?
        }
    }

数据模型

public class MenuPageItem
{
    public string Id { get; set; }
    public string PageName { get; set; }
    public string Url { get; set; }
}


public class MenuContainerItem
{
    public string Id { get; set; }
    public string ContainerName { get; set; }
    public string Icon { get; set; }
    public IList<MenuPageItem> Pages { get; set; }
}

public enum MenuType
{
    ADMIN = 0,
    USER = 1
}

接口

public interface IMenuService
{
   IList<MenuContainerItem> GetMenu(MenuType type);
}

StaticMenuService

public class StaticMenuService : IMenuService
    {
        private List<MenuContainerItem> AdminMenuContainer;
        private List<MenuContainerItem> UserMenuContainer;

        public StaticMenuService()
        {
            MenuPageItem adminPageUsers = new MenuPageItem() {
                Id = "ADMIN_PAGE_1",
                PageName = "gestione utenti",
                Url = "/admin/users"
            };
            MenuPageItem adminPageRoles = new MenuPageItem() {
                Id = "ADMIN_PAGE_2",
                PageName = "gestione ruoli",
                Url = "/admin/roles"
            };
            MenuPageItem adminPageUserRoles = new MenuPageItem() {
                Id = "ADMIN_PAGE_3",
                PageName = "gestione utenti - ruoli",
                Url = "/admin/userRoles"
            };

            MenuContainerItem AdminBaseManagerPages = new MenuContainerItem() {
                Id = "ADMIN_CONTAINER",
                Icon = "preferences",
                ContainerName = "Gestione",
                Pages = new List<MenuPageItem>() { adminPageUsers, adminPageRoles, adminPageUserRoles }
            };

            AdminMenuContainer = new List<MenuContainerItem>() { AdminBaseManagerPages };

            UserMenuContainer = new List<MenuContainerItem>();
        }

        public IList<MenuContainerItem> GetMenu(MenuType type)
        {
            if(type == MenuType.ADMIN)
            {
                return AdminMenuContainer.AsReadOnly();
            } 

            if(type == MenuType.USER)
            {
                return UserMenuContainer.AsReadOnly();
            }

            return new List<MenuContainerItem>().AsReadOnly();
        }
    }

感谢您的帮助,建议或解释。

2 个答案:

答案 0 :(得分:2)

  

我认为是修改“ StaticMenuService”构造函数并添加两个参数(UserMenu,AdminMenu),但是在那种情况下,我只修改了一个真正的类以创建测试,我认为这是错误的,不是吗?

我认为这个想法没有什么问题,我什至会认为它是对的!一旦传递菜单项而不是内部构造,您的菜单将更加干净。您还可以自由更改菜单项,而无需修改菜单。

public class StaticMenuService : IMenuService
{
    private List<MenuContainerItem> AdminMenuContainer;
    private List<MenuContainerItem> UserMenuContainer;

    public StaticMenuService(List<MenuContainerItem> adminMenus, List<MenuContainerItem> userMenus)
    {
        AdminMenuContainer = adminMenus;
        UserMenuContainer = userMenus;
    }

    public IList<MenuContainerItem> GetMenu(MenuType type)
    {
        if(type == MenuType.ADMIN)
        {
            return AdminMenuContainer.AsReadOnly();
        } 

        if(type == MenuType.USER)
        {
            return UserMenuContainer.AsReadOnly();
        }

        return new List<MenuContainerItem>().AsReadOnly();
    }
}

测试变得非常简单,只需在构造函数中传递假实例即可。至于如何处理我们剥离的硬编码菜单,请将它们放在负责定义角色菜单的某些外部类中。有百万种方法可以执行此操作,下面是一个示例:

public class AdminMenus 
{

    public static implicit operator List<MenuContainerItem> (AdminMenus menus) 
    {
        MenuPageItem adminPageUsers = new MenuPageItem() {
            Id = "ADMIN_PAGE_1",
            PageName = "gestione utenti",
            Url = "/admin/users"
        };
        MenuPageItem adminPageRoles = new MenuPageItem() {
            Id = "ADMIN_PAGE_2",
            PageName = "gestione ruoli",
            Url = "/admin/roles"
        };
        MenuPageItem adminPageUserRoles = new MenuPageItem() {
            Id = "ADMIN_PAGE_3",
            PageName = "gestione utenti - ruoli",
            Url = "/admin/userRoles"
        };

        MenuContainerItem AdminBaseManagerPages = new MenuContainerItem() {
            Id = "ADMIN_CONTAINER",
            Icon = "preferences",
            ContainerName = "Gestione",
            Pages = new List<MenuPageItem>() { adminPageUsers, adminPageRoles, adminPageUserRoles }
        };

        return new List<MenuContainerItem>() { AdminBaseManagerPages };
    }

} 

public class UserMenus 
{
    public static implicit operator List<MenuContainerItem> (UserMenus menus) 
    {
        return new List<MenuContainerItem>();
    }
}

现在您可以这样称呼它:

var menuService = new StaticMenuService(new AdminMenus(), new UserMenus());

看看StaticMenuService变成了什么,您可以用dictionary或某些使用MenuType作为键并返回菜单容器列表的lookup object来代替服务项目。这将消除对if语句的需求,而if语句可能成为噩梦般的维持下去。

答案 1 :(得分:0)

如果我了解您的意思,那么我相信您可能会受益于创建Abstract Class而不是菜单服务接口。

这样,您可以实例化试图传递到单元测试中的菜单服务的类型。

例如:

public abstract class MenuService
{
    public MenuService(List<MenuContainerItem>() container)
    {
        // do shared container setup 
    }
}

public class AdminMenuService : MenuService
{
    public AdminMenuService(List<MenuContainerItem>() container) : base(container)
    {
    }
}

然后,在单元测试中,您可以执行以下操作:

var adminMenuService = new AdminMenuService()
var userMenuService = new UserMenuService()

[TestCase(adminMenuService)]
[TestCase(userMenuService)]
public void IMenuServiceReturnsAlwaysAList(MenuService menuService)
{
    menuService.getMenu() // test what you expect to happen in all MenuServices
}