2
\$\begingroup\$

I'm trying to learn and write best practice unittests and practice test driven design. Can you give me some tips and review my code?

using System.Threading.Tasks;
using Moq;
using Xunit;

namespace Blockchain.Unittests
{
    public class AccountShould
    {
        private static string _webUrl = "https://test.test/test";

        [Fact]
        public async Task ReturnAccountBalance4EthWhenGivenAddress()
        {
            // Arrange
            var address = AccountData.Address;
            var web3RepositoryMock = new Mock<IWeb3Repository>();

            web3RepositoryMock
                .Setup(web3RepositoryMock => web3RepositoryMock.GetAccountBalanceInEth(It.IsAny<string>()))
                .ReturnsAsync(4);

            var account = new Account(web3RepositoryMock.Object);
            
            // Act
            var balance = await account.GetBalanceInEth(address);

            // Assert
            Assert.Equal(4, balance);
        }
    }
}

https://github.com/mnirm/learning-blockchain/blob/feature/test-get-account-balance/Blockchain.Unittests/AccountShould.cs

\$\endgroup\$
10
  • \$\begingroup\$ In your test you are testing moq :) \$\endgroup\$ Commented Nov 11, 2021 at 13:41
  • \$\begingroup\$ Normally when we write unit tests then we make assertions against ClassA's functionality, which relies on ClassB. We are mocking ClassB in order to focus only on ClassA's code by making ClassB's response deterministic and predictable. \$\endgroup\$ Commented Nov 11, 2021 at 13:45
  • \$\begingroup\$ In your case accountStub is the mocked ClassB. But you don't have ClassA in your test. \$\endgroup\$ Commented Nov 11, 2021 at 13:47
  • \$\begingroup\$ @PeterCsala so I should moq the Web3 class? \$\endgroup\$ Commented Nov 11, 2021 at 13:59
  • \$\begingroup\$ @PeterCsala what do you recommend me, how should I write this test then? I'm trying to learn and write good unit tests \$\endgroup\$ Commented Nov 11, 2021 at 13:59

1 Answer 1

1
\$\begingroup\$

Your GetBalanceInEth is a wrapper around the GetAccountBalanceInEth call. That's why you can't test too much things.

But this here are my advices:

  • Rename your test to follow the Given-When-Then pattern
  • Preserve 4 in a const so you can express the intent that Account does not modify that value
public async Task GivenAValidAddressAndAFlawlessRepository_WhenICallGetBalanceInEth_ThenItReturnsTheOutputWithoutModification()
{
    // Arrange
    const decimal expectedBalance = 4;
    var address = AccountData.Address;
    var web3RepositoryMock = new Mock<IWeb3Repository>();

    web3RepositoryMock
        .Setup(web3RepositoryMock => web3RepositoryMock.GetAccountBalanceInEth(It.IsAny<string>()))
        .ReturnsAsync(expectedBalance);

    var account = new Account(web3RepositoryMock.Object);
    
    // Act
    var balance = await account.GetBalanceInEth(address);

    // Assert
    Assert.Equal(expectedBalance, balance);
}
  • Write a test to make sure that the your GetAccountBalanceInEth does not swallow exception
public async Task GivenAnInvalidAddressAndAFlaultyRepository_WhenICallGetBalanceInEth_ThenItThrowsUriFormatException()
{
    // Arrange
    string errorMessage = "The provided url is malformed";
    UriFormatException toBeThrownException = new UriFormatException(errorMessage);
    var address = AccountData.Address;
    var web3RepositoryMock = new Mock<IWeb3Repository>();

    web3RepositoryMock
        .Setup(web3RepositoryMock => web3RepositoryMock.GetAccountBalanceInEth(It.IsAny<string>()))
        .ThrowsAsync(toBeThrownException);

    var account = new Account(web3RepositoryMock.Object);
    
    // Act
    Func<Task> act = () => account.GetBalanceInEth(address);

    // Assert
    UriFormatException ex = await Assert.ThrowsAsync<UriFormatException>(act);
    Assert.Contains(errorMessage, ex.Message);
}
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.