MS 单元测试示例

发布于 2024-10-07 09:31:05 字数 994 浏览 0 评论 0原文

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

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

//Arrange
string email = "[email protected]";
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??????

I'm struggling to get my head around unit testing. I've been following examples from both Nerd dinner and pro asp.net MVC framework, but as soon as I attempt my own quickly become stuck. As a test I have tried to build a validation class that simply using login information - usename and password to return a user from a repository. I've used this example as it's pretty simple to imagine what tests one might perform: Is_Password_Valid, Is_Username_Valid etc.

I've spent far too long trying to get my noggin around this. Could any one provide an example of how they might approach this as a unit test? I think once I've cracked this I'll be away.

//Arrange
string email = "[email protected]";
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??????

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绝情姑娘 2024-10-14 09:31:05

看来您确实走在正确的轨道上,但让我尝试解释一下我将如何进行测试。

实际的被测系统 (SUT) 是验证类。您还没有透露太多相关内容,因此我将假设以下内容:

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

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

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

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

public void LoginWillReturnTrueForAValidUsernamePasswordCombination()
{
    string email = "[email protected]";
    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 = "[email protected]";
    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 = "[email protected]";
    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 方法将引发异常叫。

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

It definitely looks like you are on the right track, but let me try to explain how I would go about the test.

The actual system under test (SUT) here is the Authenticate class. You have not told a lot about it, so I will assume the following:

It uses an instance of ICustomerRepository to determine the existence of a user based on a combination of username (email) and password.

When the repostiory returns an instance of Customer, given a username and password combination, the Login method returns true. When the repository returns null, the Login method returns false.

I will use these assumptions in the following, but if they are not correct, I´m sure you will be able to change the tests so that they make sense for your scenario.

Test 1: When the username/password combination is correct, Login will return true

public void LoginWillReturnTrueForAValidUsernamePasswordCombination()
{
    string email = "[email protected]";
    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);
}

Note the use of It.Is. Basically, the mock is set up in a way that it will only return the dummy customer object when the email and password defined in your test are passed to the GetCustomerByPasswordUsername method.

Test 2: When the username/password combination is incorrect, Login will return false

public void LoginWillReturnFalseForAnInvalidUsernamePasswordCombination()
{
    string email = "[email protected]";
    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);
}

Although implicitly tested by the above tests, you may want to go a step further and write a test that ensures that the Login method passes the right parameters to the repository. Such a test could look like the following:

Test 3: Login will invoke repository correctly

public void LoginWillInvokeGetCustomerByPasswordUsernameCorrectly()
{
    string email = "[email protected]";
    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();
}

The Verify method of a mock throws an exception if the methods that have been setup have not been called.

I hope this helps. Don't hesitate to ask if you have further questions.

对你的占有欲 2024-10-14 09:31:05

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

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 的可运行对象到计时器,您可以按顺序和相反顺序运行它们,以确保您的系统正常工作。

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

If you happen to have a System Under Test that uses an API that your SUT adds a listener too, it can get a bit more complicated as well(You can do this same thing with executors and timers(mock both)).

class RealAPIYouSUTUses {
      public void addListener(XXXListener l);
}

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

Then your unit test can test the listesner and it's interaction with the system...

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"));
    }
}

The interesting thing about all this is you can mock Executor.java and Timer.java and your test can grab the runnable that was given to an executor and the TimerTaks to the Timer and you can run them in order and in reverse order to make sure your system works.

I do alot of asynchronous programming and unit testing like this rocks so you can make sure integration between all your classes is working.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文