Поблочное тестирование на исключения в конструкторе Python

Вы движетесь в правильном направлении, пользователь и документ являются совокупными, поскольку они созданы в отдельных транзакциях. Когда речь заходит о том, кто на кого ссылается, принцип масштабируемости IDDD гласит, что агрегаты должны ссылаться на агрегаты только через их идентификаторы.

Я думаю, что придерживаясь повсеместного языка, ваш код должен выглядеть примерно так

class User {
    private UserId id;
    private String name;    

    User(String name) {
        this.id = new UserId();
        this.name = name;            
    }

    Document createDocument(String name) {
        Document document = new Document(name);        
        document.createdBy(this);
        return document;
    }

    Document approve(Document document) {
        document.approved();
        return document;
    }
}

class Document {
    private DocumentId id;
    private String name;
    private UserId userId;
    private Boolean isApproved;

    Document(String name) {
        this.id = new DocumentId();
        this.name = name;
    }

    void createdBy(UserId userId) {
        this.userId = userId;
    }    

    void approved() {
        this.isApproved = true;
    }

}

// User creation
User user = new User("Someone");
userRepository.save(user);

//Document creation
User user = userRepository.find(new UserId("some-id"))
Document document = user.createDocument("important-document")
documentRepository.save(document)

// Approval
User user = userRepository.find(new UserId("some-id"))
Document document = documentRepository.find(new DocumentId("some-id")) 
document = user.approve(Document)

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

11
задан Vemonus 16 May 2017 в 21:41
поделиться

5 ответов

Если Вы только хотите проверить, повышает ли конструктор исключение, то намного лучше использовать лямбду:

    def testInsufficientArgs(self):
        self.assertRaises(ValueError, lambda: MyClass(0))

Как это, аргументы конструктора не установлены "волшебно" (как в ответе @Miles'), и IDE может всегда говорить Вам, где конструктор используется.

0
ответ дан 3 December 2019 в 03:36
поделиться

In the unittest module, one can test (with assertRaises) for specific exception when a callable is called, but obviously that will apply to methods of the class. What is the proper way to run such test for the constructor?

The constructor is itself a callable:

self.assertRaises(AssertionError, MyClass, arg1, arg2)

That being said, I want to echo nosklo's and S.Lott's concerns about doing type checking of arguments. Furthermore, you shouldn't be using assertions to check function arguments: assertions are most useful as sanity checks that won't get triggered unless something is internally wrong with your code. Also, assert statements are compiled out when Python is run in "optimized" -O mode. If a function needs to do some sort of checking of its arguments, it should raise a proper exception.

17
ответ дан 3 December 2019 в 03:36
поделиться

Не связывайтесь с assertRaises. Это слишком сложно.

Сделайте это

class Test_Init( unittest.TestCase ):
    def test_something( self ):
        try:
            x= Something( "This Should Fail" )
            self.fail( "Didn't raise AssertionError" )
        except AssertionError, e:
            self.assertEquals( "Expected Message", e.message )
            self.assertEquals( args, e.args )

Любое другое исключение будет обычной тестовой ошибкой.

Кроме того, не связывайтесь с слишком частой предварительной проверкой ошибок в методе __ init __ . Если кто-то предоставит объект неправильного типа, ваш код выйдет из строя при нормальном ходе событий и вызовет обычное исключение обычными средствами. Вам не нужно так много "предварительно просматривать" объекты.

7
ответ дан 3 December 2019 в 03:36
поделиться

Well, as a start, checking for bad arguments is not a good idea in python. Python is dynamically strong typed for a reason.

You should just assume that the arguments are good arguments. You never know your class' users' intent so by checking for good arguments is a way to limit usage of your class in more generic instances.

Instead define a good API and document it very well, using docstrings and text, and leave errors of bad arguments flow automatically to the user.

Example:

def sum_two_values(value_a, value_b):
    return value_a + value_b

okay, this example is stupid, but if I check and assert the value as integer, the function won't work with floats, strings, list, for no reason other than my check, so why check in first place? It will automatically fail with types that it wouldn't work, so you don't have to worry.

1
ответ дан 3 December 2019 в 03:36
поделиться

Ответ С.Лотта неверен: self.fail () сам вызывает исключение, которое затем будет вызвано исключением в следующей строке:

class NetworkConfigTest1(unittest.TestCase):
    def runTest(self):
        try:
            NetworkConfig("192.168.256.0/24")
            self.fail("Exception expected but not thrown")
        except Exception, error:
            printf("Exception caught: %s" % str(error)
            pass

вывод был «Ожидается исключение, но не сгенерировано», но модульный тест не был помечен как сбой, хотя тестируемый код не был написан!

Более надежный способ проверить, вызывает ли метод исключение, было бы использование:

self.failUnlessRaises([error], [callable], [arguments to callable])

В моем случае класс, который я тестирую, называется NetworkConfig , и конструктор должен генерировать исключение, если сетевой дескриптор недействителен. И вот что сработало:

class NetworkConfigTest1(unittest.TestCase):
    def runTest(self):
        self.failUnlessRaises(Exception, NetworkConfig, "192.168.256.0/24")

Это работает должным образом и выполняет правильный тест.

0
ответ дан 3 December 2019 в 03:36
поделиться
Другие вопросы по тегам:

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