正确实施单元测试

时间:2013-11-11 14:24:00

标签: c# unit-testing signalr

我第一次练习单元测试,我有一些问题。我将通过解释我试图测试的内容开始。

我想测试一个看起来像这样的方法:

public bool IsAdmin(HubCallerContext hubCallerContext)
{
    return hubCallerContext.User.IsInRole("admin");
}

该方法在类UserService中实现,该类连接到接口IUserService

我试图创建2个测试:

  • 一个HubCallerContext,其作用是" admin"并将断言是真的。
  • 一个HubCallerContext,其作用是" user"并将断言是假的。

我已经在我的解决方案中创建了一个新的类库,在那里我已经参考了我正在测试的项目。我已经安装了NUnit和Moq,并创建了一个如下所示的测试类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ChatProj;
using NUnit.Framework;
using ChatProj.Controllers;
using Moq;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using ChatProj.DAL;
using ChatProj.Service_Layer;
using System.Threading.Tasks;

namespace ChatProj.Tests
{
    [TestFixture]
    public class Class1
    {

        [SetUp]
        public void Setup()
        {

        }

        [Test]
    public void IsAdmin_CalledByAdmin_ReturnTrue()
    {
        UserService userService = new UserService();
        bool result = userService.IsAdmin( ? );
        Assert.IsTrue( result, "Something is wrong." );
    }

    [Test]
    public void IsAdmin_CalledByUser_ReturnFalse()
    {
        UserService userService = new UserService();
        bool result = userService.IsAdmin( ? );
        Assert.IsFalse( result, "Something is wrong." );
    }

    }
}

在这里,我开始感到困惑。 (我用&#34标记了IsAdmin呼叫的参数;?"因为我不确定放在那里的内容。)

我已经读过关于嘲笑,存根,假货和假人的内容,但这些定义是为了让我真正掌握。我已经找到了这些定义,例如:

 - Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.

 - Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).

 - Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.

 - Mocks are objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

由于我设计了我的测试类,我需要对HubCallerContext进行某种替换。这是假设我正在测试" IsAdmin"方法正确。

所以我的问题是:

3 个答案:

答案 0 :(得分:4)

假设HubCallerContext就是这个 - https://github.com/SignalR/SignalR/blob/master/src/Microsoft.AspNet.SignalR.Core/Hubs/HubCallerContext.cs - 那么设置测试将很容易。你只需要两个IPrincipal的模拟,其中一个为.IsInRole("admin")调用返回true,另一个返回false.wrap这两个模拟IRequest

语法将根据使用的模拟框架而有所不同,但您的测试最终会像:

[Test]
public void IsAdmin_CalledByAdmin_ReturnTrue()
{
    UserService userService = new UserService();
    var principalMock = new Mock<IPrincipal>();
    principalMock.Setup(x => x.IsInRole("admin")).Returns(true);
    var requestMock = new Mock<IRequest>();
    requestMock.Setup(x => x.User).Returns(principalMock.Object);
    var result = userService.IsAdmin(new HubCallerContext(requestMock.Object, ""));
    Assert.IsTrue( result, "Something is wrong." );
}

[Test]
public void IsAdmin_CalledByUser_ReturnFalse()
{
    UserService userService = new UserService();
    var principalMock = new Mock<IPrincipal>();
    principalMock.Setup(x => x.IsInRole("admin")).Returns(false);
    var requestMock = new Mock<IRequest>();
    requestMock.Setup(x => x.User).Returns(principalMock.Object);
    var result = userService.IsAdmin(new HubCallerContext(requestMock.Object, ""));
    Assert.IsFalse( result, "Something is wrong." );
}

我没有检查上面是否编译,但它是基于Moq所需的语法。

答案 1 :(得分:3)

我认为如果您稍微更改测试方法(假设它不是遗留代码的一部分),那么编写这两个单元测试会更容易。

如果您以这种方式定义方法:

public bool IsAdmin(IPrincipal user)
{
    return user.IsInRole("admin");
}
事情会变得非常简单(顺便说一下。查看“得墨忒耳法则”;))。您可以传入一个模拟对象(因为用户参数是一个接口 - IPrincipal),如果用户应该处于“admin”角色,则返回true,否则返回false。

此解决方案的好处是您不必构建模拟对象的图形,并且测试的排列部分非常简单。你的测试看起来有点像这样:

    [Test]
    public void IsAdmin_CalledByAdminUser_ReturnTrue()
    {
        //Arrange
        var principalMock = new Mock<IPrincipal>();
        principalMock.Setup(x => x.IsInRole("admin")).Returns(true);

        //Act
        var userService = ...// create an instance of userService here
        var result = userService.IsAdmin(principalMock);

        //Assert
        Assert.IsTrue(result);
    }

    [Test]
    public void IsAdmin_CalledByNonAdminUser_ReturnFalse()
    {
        //Arrange
        var principalMock = new Mock<IPrincipal>();
        principalMock.Setup(x => x.IsInRole("admin")).Returns(false);

        //Act
        var userService = ...// create an instance of userService here
        var result = userService.IsAdmin(principalMock);

        //Assert
        Assert.IsFalse(result);
    }

我建议你阅读这一系列的博文(我认为这很酷:)):http://www.daedtech.com/tag/unit-testing

答案 2 :(得分:-2)

您可以使用foreach获取角色列表并检查每个角色。