Как я мог осуществить рефакторинг этот метод типа фабрики, и база данных звонят, чтобы быть тестируемым?

одно простое решение должно было бы записать программу, чтобы сделать это для Вас. Это могло, вероятно, быть сделано довольно быстро в Python, чем-то как:

sum = 0
file = open("numbers.txt","R")
for line in file.readlines(): sum+=int(line)
file.close()
print sum

я не протестировал тот код, но это выглядит правильным. Просто измените numbers.txt на название файла, сохраните код в файл, названный sum.py, и в консольном типе в "Python sum.py"

12
задан azheglov 5 August 2009 в 17:49
поделиться

6 ответов

Вы правы, говоря о перемещении GetAgentFromDatabase () в отдельный класс . Вот как я переопределил AgentRepository :

public class AgentRepository {
    private IAgentDataProvider m_provider;

    public AgentRepository( IAgentDataProvider provider ) {
        m_provider = provider;
    }

    public Agent GetAgent( int agentId ) {
        Agent agent = null;
        using( IDataReader agentDataReader = m_provider.GetAgent( agentId ) ) {
            if( agentDataReader.Read() ) {
                agent = new Agent();
                // set agent properties later
            }
        }
        return agent;
    }
}

где я определил интерфейс IAgentDataProvider следующим образом:

public interface IAgentDataProvider {
    IDataReader GetAgent( int agentId );
}

Итак, AgentRepository - это тестируемый класс. Мы имитируем IAgentDataProvider и внедрим зависимость. (Я сделал это с помощью Moq , но вы можете легко повторить это с помощью другой структуры изоляции).

[TestFixture]
public class AgentRepositoryTest {
    private AgentRepository m_repo;
    private Mock<IAgentDataProvider> m_mockProvider;

    [SetUp]
    public void CaseSetup() {
        m_mockProvider = new Mock<IAgentDataProvider>();
        m_repo = new AgentRepository( m_mockProvider.Object );
    }

    [TearDown]
    public void CaseTeardown() {
        m_mockProvider.Verify();
    }

    [Test]
    public void AgentFactory_OnEmptyDataReader_ShouldReturnNull() {
        m_mockProvider
            .Setup( p => p.GetAgent( It.IsAny<int>() ) )
            .Returns<int>( id => GetEmptyAgentDataReader() );
        Agent agent = m_repo.GetAgent( 1 );
        Assert.IsNull( agent );
    }

    [Test]
    public void AgentFactory_OnNonemptyDataReader_ShouldReturnAgent_WithFieldsPopulated() {
        m_mockProvider
            .Setup( p => p.GetAgent( It.IsAny<int>() ) )
            .Returns<int>( id => GetSampleNonEmptyAgentDataReader() );
        Agent agent = m_repo.GetAgent( 1 );
        Assert.IsNotNull( agent );
                    // verify more agent properties later
    }

    private IDataReader GetEmptyAgentDataReader() {
        return new FakeAgentDataReader() { ... };
    }

    private IDataReader GetSampleNonEmptyAgentDataReader() {
        return new FakeAgentDataReader() { ... };
    }
}

(Я не учел реализацию класса FakeAgentDataReader , который реализует IDataReader и является тривиальным - вам нужно только реализовать Read () и Dispose () , чтобы тесты работали.)

Цель of AgentRepository здесь, чтобы взять объекты IDataReader и преобразовать их в правильно сформированные объекты Agent . Вы можете расширить вышеуказанное тестовое устройство, чтобы проверить более интересные случаи.

После модульного тестирования AgentRepository изолированно от реальной базы данных вам потребуются модульные тесты для конкретной реализации IAgentDataProvider ], но это тема отдельного вопроса. HTH

Вы можете расширить вышеуказанное тестовое устройство, чтобы проверить более интересные случаи.

После модульного тестирования AgentRepository изолированно от реальной базы данных вам потребуются модульные тесты для конкретной реализации IAgentDataProvider ], но это тема отдельного вопроса. HTH

Вы можете расширить вышеуказанное тестовое устройство, чтобы проверить более интересные случаи.

После модульного тестирования AgentRepository изолированно от реальной базы данных вам потребуются модульные тесты для конкретной реализации IAgentDataProvider ], но это тема отдельного вопроса. HTH

9
ответ дан 2 December 2019 в 22:23
поделиться

Проблема состоит в том, чтобы решить, что такое SUT, а что - Test. В вашем примере вы пытаетесь протестировать метод Select () и поэтому хотите изолировать его от базы данных. У вас есть несколько вариантов:

  1. Виртуализировать GetAgentFromDatabase () , чтобы вы могли предоставить производный класс с кодом для возврата правильных значений, в этом случае создавая объект, который предоставляет IDataReaderFunctionaity без общение с БД, т.е.

     класс MyDerivedExample: YourUnnamedClass
    {
     защищенное переопределение IDataReader GetAgentFromDatabase ()
     {
     вернуть новый MyDataReader ({"AgentId", "1"}, {"FirstName", "Fred"},
     ...);
     }
    }
    
  2. Как Гишу предложил вместо использования отношений IsA (наследование), используйте HasA (композиция объектов), где у вас снова есть класс, который обрабатывает создание макета IDataReader , но на этот раз без наследование.

    Однако оба из них приводят к созданию большого количества кода, который просто определяет набор результатов, которые мы возвращаем при запросе. По общему признанию, мы можем сохранить этот код в тестовом коде вместо нашего основного кода, но это усилия. Все, что вы на самом деле делаете, это определяете набор результатов для конкретных запросов, и вы знаете, что действительно хорошо для этого ... База данных

  3. Некоторое время назад я использовал LinqToSQL и обнаружил, что объекты DataContext имеют несколько очень полезных методов, включая DeleteDatabase и CreateDatabase . [FixtureSetUp ()] public void Setup () { Контекст OARsDataContext = новый MyAppDataContext (UnitTestConnection); если (context.DatabaseExists ()) { Console.WriteLine («Удаление существующей тестовой базы данных»); context.DeleteDatabase (); } Console.WriteLine («Создание новой тестовой базы данных»); context.CreateDatabase (); context.SubmitChanges (); }

Подумайте над этим. Проблема с использованием базы данных для модульных тестов заключается в том, что данные изменятся. Удалите свою базу данных и используйте свои тесты для развития ваших данных, которые можно будет использовать в будущих тестах.

Следует остерегаться двух вещей. Убедитесь, что ваши тесты выполняются в правильном порядке. Синтаксис MbUnit для этого - [DependsOn ("NameOfPreviousTest")] . Убедитесь, что для конкретной базы данных выполняется только один набор тестов.

1
ответ дан 2 December 2019 в 22:23
поделиться

я начну выдвигать некоторые идеи и обновлю попутно:

  • SqlDatabase sqlDb = new SqlDatabase ("MyConnectionString"); - Вам следует избегать смешивания операторов new с логикой. Вы должны построить xor с логическими операциями; избегайте их одновременного появления. Используйте внедрение зависимости, чтобы передать эту базу данных в качестве параметра, так что вы можете издеваться над этим. Я имею в виду это, если вы хотите провести модульное тестирование (не переходя к базе данных, что должно быть сделано в некоторых случаях позже)
  • IDataReader agentInformation = GetAgentFromDatabase (agentId) - Возможно, вы могли бы разделить получение Reader для какого-либо другого класса, поэтому вы можете поиздеваться над этим классом при тестировании заводского кода.
0
ответ дан 2 December 2019 в 22:23
поделиться

На мой взгляд, метод GetAgentFromDatabase () не должен проверяться дополнительным тестом, потому что его код полностью покрывается тестом метода Select (). Нет никаких ветвей, по которым мог бы пройти код, поэтому нет смысла создавать здесь дополнительный тест. Если метод GetAgentFromDatabase () вызывается из нескольких методов, вы должны протестировать его самостоятельно.

0
ответ дан 2 December 2019 в 22:23
поделиться

Предполагая, что вы пытаетесь протестировать общедоступный метод Select класса [NoName] ..

  1. Переместите метод GetAgentFromDatabase () в интерфейс, скажем, IDB_Access. Пусть NoName имеет член интерфейса, который может быть установлен как параметр ctor или свойство. Итак, теперь у вас есть шов, вы можете изменить поведение, не изменяя код в методе.
  2. Я бы изменил тип возвращаемого значения вышеупомянутого метода, чтобы вернуть что-то более общее - похоже, вы используете его как хэш-таблицу. Пусть производственная реализация IDB_Access использует IDataReader для внутреннего создания хеш-таблицы. Это также снижает зависимость от технологий; Я могу реализовать этот интерфейс, используя MySql или некоторую среду, отличную от MS / .net. private Hashtable GetAgentFromDatabase (int agentId)
  3. Далее в модульном тесте вы можете работать с заглушкой (или использовать что-то более продвинутое, например, фиктивный фреймворк)

.

public MockDB_Access : IDB_Access
{
  public const string MY_NAME = "SomeName;
  public Hashtable GetAgentFromDatabase(int agentId)
  {  var hash = new Hashtable();
     hash["FirstName"] = MY_NAME; // fill other properties as well
     return hash;
  }
}

// in the unit test
var testSubject = new NoName( new MockDB_Access() );
var agent = testSubject.Select(1);
Assert.AreEqual(MockDB_Access.MY_NAME, agent.FirstName); // and so on...
0
ответ дан 2 December 2019 в 22:23
поделиться

ИМО, вам обычно следует беспокоиться только о том, чтобы ваши общедоступные свойства / методы были проверены. Т.е. пока работает Select (int agentId) , вам обычно все равно, как он это делает через GetAgentFromDatabase (int agentId) .

То, что у вас есть, кажется разумным, как я представьте, что это можно протестировать с помощью чего-то вроде следующего (предполагая, что ваш класс называется AgentRepository)

AgentRepository aRepo = new AgentRepository();
int agentId = 1;
Agent a = aRepo.Select(agentId);
//Check a here

Что касается предлагаемых улучшений. Я бы рекомендовал разрешить изменение строки подключения AgentRepository, будь то общий или внутренний доступ.

0
ответ дан 2 December 2019 в 22:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: