【问题标题】:MS Unit testing exampleMS 单元测试示例
【发布时间】:2010-12-12 17:11:18
【问题描述】:

我正在努力理解单元测试。我一直在关注 Nerd Dinner 和 pro asp.net MVC 框架中的示例,但是一旦我尝试自己的示例,我很快就会陷入困境。作为测试,我尝试构建一个验证类,它仅使用登录信息 - 用户名和密码从存储库返回用户。我使用了这个例子,因为它很容易想象一个人可能会执行什么测试:Is_Password_Valid、Is_Username_Valid 等。

我花了太长时间试图解决这个问题。任何人都可以提供一个示例来说明他们如何将其作为单元测试来处理吗?我想一旦我破解了这个,我就会离开。

//Arrange
string email = "test@test.com";
string password = "test";

//Arrange
List<Customer> customer = new List<Customer>();

customer.Add(new Customer { CustomerId = 1, Email = email, Password = "best", FirstName = "test", LastName = "wods", Sex = true });
mockRepos = new Moq.Mock<ICustomerRepository>();
mockRepos.Setup(x => x.GetCustomerByPasswordUsername(email, password)).Returns(customer.First());
Authenticate auth = new Authenticate(mockRepos.Object);

//Act
var result = auth.Login(email, password);

//Assert
//this is where I start to become unstuck??????

【问题讨论】:

    标签: unit-testing moq


    【解决方案1】:

    看起来你肯定走在正确的轨道上,但让我试着解释一下我将如何进行测试。

    这里实际的system under test (SUT) 是Authenticate 类。你没有说太多,所以我假设如下:

    它使用ICustomerRepository 的实例根据用户名(电子邮件)和密码的组合来确定用户的存在。

    当存储库返回 Customer 的实例时,给定用户名和密码组合,Login 方法返回 true。当仓库返回null时,Login方法返回false。

    我将在下面使用这些假设,但如果它们不正确,我相信您将能够更改测试,以便它们对您的场景有意义。

    测试一:当用户名/密码组合正确时,Login会返回true

    public void LoginWillReturnTrueForAValidUsernamePasswordCombination()
    {
        string email = "test@test.com";
        string password = "test";
    
        //Dummy customer
        var customer = new Customer();
    
        //Create mock
        var mockRepos = new Moq.Mock<ICustomerRepository>();
        mockRepos.Setup(x => x.GetCustomerByPasswordUsername(
                It.Is<string>(s => s == email), 
                It.Is<string>(s => s == password))
            .Returns(customer);
    
        var auth = new Authenticate(mockRepos.Object);
    
        //Act
        var result = auth.Login(email, password);
    
        //Assert
        Assert.IsTrue(result);
    }
    

    注意It.Is 的使用。基本上,模拟的设置方式是,当您的测试中定义的电子邮件和密码传递给 GetCustomerByPasswordUsername 方法时,它只会返回虚拟客户对象。

    测试2:当用户名/密码组合不正确时,Login会返回false

    public void LoginWillReturnFalseForAnInvalidUsernamePasswordCombination()
    {
        string email = "test@test.com";
        string password = "test";
    
        //Create mock
        var mockRepos = new Moq.Mock<ICustomerRepository>();
        mockRepos.Setup(x => x.GetCustomerByPasswordUsername(
                It.Is<string>(s => s == email), 
                It.Is<string>(s => s == password))
            .Returns<Customer>(null);
    
        var auth = new Authenticate(mockRepos.Object);
    
        //Act
        var result = auth.Login(email, password);
    
        //Assert
        Assert.IsFalse(result);
    }
    

    虽然通过上述测试进行了隐式测试,但您可能希望更进一步,编写一个测试,以确保 Login 方法将正确的参数传递到存储库。这样的测试可能如下所示:

    测试 3:登录将正确调用存储库

    public void LoginWillInvokeGetCustomerByPasswordUsernameCorrectly()
    {
        string email = "test@test.com";
        string password = "test";
    
        //Create mock
        var mockRepos = new Moq.Mock<ICustomerRepository>();
        mockRepos.Setup(x => x.GetCustomerByPasswordUsername(
                It.Is<string>(s => s == email), 
                It.Is<string>(s => s == password))
            .Returns<Customer>(null)
            .Verifiable();
    
        var auth = new Authenticate(mockRepos.Object);
    
        //Act (ignore result. We are only testing correct invocation)
        auth.Login(email, password);
    
        //Assert
        mockRepos.Verify();
    }
    

    如果已经设置的方法没有被调用,模拟的Verify方法会抛出异常。

    我希望这会有所帮助。如果您还有其他问题,请随时提问。

    【讨论】:

    • 嗨克劳斯。这个信息太棒了。多么全面的答案。非常感谢!
    【解决方案2】:

    如果您碰巧有一个使用 API 的被测系统,您的 SUT 也添加了一个侦听器,它也会变得更复杂(您可以使用执行器和计时器来做同样的事情(模拟两者))。

    class RealAPIYouSUTUses {
          public void addListener(XXXListener l);
    }
    
    class MockAPI implements RealAPIYourSUTUses{
          public void addListener(XXXListener l) {
             this.cachedListener = l;
          }
          public XXXListener getListener() {
             return cachedListener;
          } 
    }
    

    然后你的单元测试可以测试监听器以及它与系统的交互......

    class Test {
        public void test() {
            MockAPI mockAPI = new MockAPI();   
            SUT sut = new SUT(mockAPI);  
    
            sut.doSomething();
            //expect your listener to be added from calling doSomething....
            XXXListener l = mockApi.getListener();
    
            //simulate firing an event into your SUT 
            l.fireEvent(new Event("asdf"));
        }
    }
    

    所有这一切的有趣之处在于您可以模拟 Executor.java 和 Timer.java 并且您的测试可以获取给执行器的可运行对象和给计时器的 TimerTaks,您可以按顺序和相反的顺序运行它们以确保您的系统正常工作。

    我做了很多这样的异步编程和单元测试,这样你就可以确保所有类之间的集成正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-03
      相关资源
      最近更新 更多