如何对方法正确地为另一个类

时间:2017-08-05 12:46:05

标签: c# unit-testing nsubstitute

我正在尝试对InsertCashTransaction类中的方法Execute()进行单元测试。我想测试它是否正确地为User.Balance分配了一个新值。你可以在这里看到两个类

InsertCashTransaction类

    public class InsertCashTransaction : Transaction
{
    private IUser _userI;
    public InsertCashTransaction(User user, DateTime date, decimal amount) : base(user, date, amount)
    {
        User = user;
    }

    public InsertCashTransaction(IUser UserI)
    {
        this._userI = UserI;
    }

    public override string ToString()
    {
        return $"Transaction number: {TransactionId}, Date: {Date}: {Amount} has been inserted onto {User.Username}'s wallet.";
    }

    // Method I am trying to test
    public override void Execute()
    {
        if (Amount > 0)
        {
            User.Balance = User.Balance + Amount;
        }
        else if (Amount <= 0)
        {
            throw new ArgumentException("Not allowed to withdraw from users balance nor insert 0");
        }
    }
}
  

用户类

public class User : IUser 
{
    private int _userid;
    private string _firstname;
    private string _lastname;
    private string _username;
    private string _email;
    private decimal _balance;

    public int UserID
    {
        get { return _userid; }
        set
        {
            if (value < 1)
            {
                throw new ArgumentException("ID cannot be below one");
            }
            _userid = value;
        }
    }

    public string FirstName
    {
        get { return _firstname; }
        set
        {
            CheckIfNull(value);
            ValidateName(value);
            _firstname = value;
        }
    }

    public string LastName
    {
        get { return _lastname; }
        set
        {
            CheckIfNull(value);
            ValidateName(value);
            _lastname = value;
        }
    }

    public string Username
    {
        get { return _username; }
        set
        {

            CheckIfNull(value);
            foreach (char item in value)
            {
                if (char.IsUpper(item))
                {
                    throw new ArgumentException("Username is not allowed to hold use upper case letter");
                }

                if (char.IsSymbol(item))
                {
                    throw new ArgumentException("Username must not contains symbols");
                } else if (item == '-')
                {
                    throw new ArgumentException("Username must not contain symbols");
                }
            }
            _username = value;
        }
    }

    public string Email
    {
        get { return _email; }
        set
        {
            CheckIfNull(value);
            //Creates two out of the email separated by @
            string[] separation = value.Split('@');
            string localPart = separation[0];
            string domain = separation[1];
            foreach (char item in localPart)
            {
                if (char.IsLetterOrDigit(item) == false)
                {
                    if (item != '.' || item != '-' || item != '_' || item != ',')
                    {
                        continue;
                    }
                    else
                    {
                        throw new ArgumentException("Not a valid email");
                    }
                }
            }
            // Check if domain starts with '.' or '-'
            if (domain.Contains("."))
            {
                if (domain.StartsWith(".") || domain.StartsWith("-") || domain.EndsWith(".") || domain.EndsWith("-"))
                {
                    throw new ArgumentException("domain must not start with .");
                }
            }
            foreach (char item in domain)
            {
                if (char.IsSymbol(item))
                {
                    throw new ArgumentException("Domain must not contain any symbols");
                }
            }
            _email = value;
        }
    }

    public decimal Balance
    {
        get { return _balance; }
        set
        {
            if (value < 0)
            {
                throw new ArgumentException("Balance is below 0");
            }
            _balance = value;
        }
    }

    public override string ToString()
    {
        return $"{FirstName}, {LastName}, {Email}";
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }
        if (this.GetType() != obj.GetType())
        {
            return false;
        }
        return Equals((User)obj);
    }

    public bool Equals(User obj)
    {
        if (obj == null)
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        if (this.GetHashCode() != obj.GetHashCode())
        {
            return false;
        }
        System.Diagnostics.Debug.Assert(base.GetType() != typeof(object));
        if (!base.Equals(obj))
        {
            return false;
        }
        return UserID.Equals(obj.UserID);
    }

    public override int GetHashCode()
    {
        return UserID.GetHashCode();
    }

    public int CompareTo(User user)
    {
        if (UserID > user.UserID)
        {
            return -1;
        }
        return 1;
    }

    public User(int id, string firstName, string lastName, string username, string email, decimal balance)
    {
        UserID = id;
        FirstName = firstName;
        LastName = lastName;
        Username = username;
        Email = email;
        Balance = balance;
    }

    public User()
    {
    }

    public string CheckIfNull(string element)
    {
        if (string.IsNullOrEmpty(element))
        {
            throw new ArgumentNullException("Something is missing");
        }
        return element;
    }

    protected string ValidateName(string name)
    {
        foreach (char item in name)
        {
            if (char.IsDigit(item))
            {
                throw new ArgumentException("Something is wrong in either firstname or lastname");
            }
        }
        return name;
    }
}

我到目前为止尝试创建User类的接口,并且通过Nsubsitute尝试在测试中替换类,您可以在这里看到

Iuser界面

    public interface IUser
{
    int UserID { get; set; }
    string FirstName { get; set; }
    string LastName { get; set; }
    string Username { get; set; }
    string Email { get; set; }
    decimal Balance { get; set; }
}

InsertCashTransactionTest类

    [TestFixture]
class InsertCashTransactionTest
{
    [TestCase(0)]
    [TestCase(-1)]
    [TestCase(-10)]
    [TestCase(-50)]
    public void AmountBelowZero_throwException(decimal number)
    {
        IUser user = Substitute.For<IUser>();
        InsertCashTransaction icTransaction = new InsertCashTransaction(user);
        icTransaction.Amount = number;
        Assert.Catch<ArgumentException>(() => icTransaction.Execute());
    }

    // Test that isn't working
    [TestCase(1)]
    [TestCase(10)]
    [TestCase(50)]
    public void AmountAboveZero_InsertToUserBalance(decimal number)
    {
        //Arrange 
        IUser user = Substitute.For<IUser>();
        InsertCashTransaction icTransaction = new InsertCashTransaction(user);
        user.Balance = 0;
        icTransaction.Amount = number;
        decimal actualresult = number;
        // Act
        // Somewhere here it goes wrong
        icTransaction.Execute();
        //Assert
        Assert.AreEqual(actualresult, user.Balance);

    }

我的实际问题 当我尝试运行测试时,我得到了一个N​​ullreference异常,我的问题是我不知道我在哪里做错了什么。每当调用icTransaction.Execute()时,问题似乎都出现了。我希望你能帮助我弄清楚我做错了什么。

请询问是否有不清楚的地方,需要进一步解释

2 个答案:

答案 0 :(得分:1)

从发布的代码中我不认为在这种情况下需要模拟User。模拟主要是替代由于某些副作用,非确定性,依赖性或速度而难以测试的类。  示例包括发送电子邮件,或要求完整的数据库设置,或模拟不同的网络条件等。

除了解决所提出的问题@Cleriston之外,我建议删除IUser接口,而是使用真实的User实例。然后,您可以使用以下内容为实际逻辑编写测试:

[TestCase(1)]
[TestCase(10)]
[TestCase(50)]
public void AmountAboveZero_InsertToUserBalance(decimal number)
{
    //Arrange 
    var user = new User(1, "FirstName", "Surname", "tst", "tst@example.com", 0);
    var icTransaction = new InsertCashTransaction(user, DateTime.Now, number);
    // Act
    icTransaction.Execute();
    //Assert
    Assert.AreEqual(number, user.Balance);
}

答案 1 :(得分:0)

你似乎有范围问题。

  • 您的构造函数需要:User,Date和Amount作为参数。当您仅使用User初始化类时,其他参数将为null。看看吧。

    [TestCase(1)]
    [TestCase(10)]
    [TestCase(50)]
    public void AmountAboveZero_InsertToUserBalance(decimal number)
    {
        //Arrange 
        IUser user = Substitute.For<IUser>();
        InsertCashTransaction icTransaction = new InsertCashTransaction(user, null, number);
        user.Balance = 0;
        decimal actualresult = number;
        // Act
        // Somewhere here it goes wrong
        icTransaction.Execute();
        //Assert
        Assert.AreEqual(actualresult, user.Balance);
    }
    

注意避免使用本机类型名称声明变量。例如:数字,布尔值,整数等

  • 引用父母的用户群(良好做法和清洁代码):

    if (base.Amount <= 0)
    {
        throw new ArgumentException("Not allowed to withdraw from users balance nor insert 0");
    }
    User.Balance = User.Balance + base.Amount;
    

当您迷路时可能出现错误。检查控制台的输出,您可以在其中找到错误的文件,行和位置。另外,共享错误输出错误也可以更轻松地跟踪您的问题。